xref: /plan9/sys/src/cmd/ip/ftpd.c (revision b3994199f6e7550c339372bfdf4b83388358d919)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include <ip.h>
67dd7cddfSDavid du Colombier #include <libsec.h>
79a747e4fSDavid du Colombier #include <String.h>
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier #include "glob.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier enum
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier 	/* telnet control character */
147dd7cddfSDavid du Colombier 	Iac=		255,
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier 	/* representation types */
177dd7cddfSDavid du Colombier 	Tascii=		0,
187dd7cddfSDavid du Colombier 	Timage=		1,
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier 	/* transmission modes */
217dd7cddfSDavid du Colombier 	Mstream=	0,
227dd7cddfSDavid du Colombier 	Mblock=		1,
237dd7cddfSDavid du Colombier 	Mpage=		2,
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier 	/* file structure */
267dd7cddfSDavid du Colombier 	Sfile=		0,
277dd7cddfSDavid du Colombier 	Sblock=		1,
287dd7cddfSDavid du Colombier 	Scompressed=	2,
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier 	/* read/write buffer size */
317dd7cddfSDavid du Colombier 	Nbuf=		4096,
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier 	/* maximum ms we'll wait for a command */
347dd7cddfSDavid du Colombier 	Maxwait=	1000*60*30,		/* inactive for 30 minutes, we hang up */
357dd7cddfSDavid du Colombier 
369a747e4fSDavid du Colombier 	Maxerr=		128,
377dd7cddfSDavid du Colombier 	Maxpath=	512,
387dd7cddfSDavid du Colombier };
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier int	abortcmd(char*);
417dd7cddfSDavid du Colombier int	appendcmd(char*);
427dd7cddfSDavid du Colombier int	cdupcmd(char*);
437dd7cddfSDavid du Colombier int	cwdcmd(char*);
447dd7cddfSDavid du Colombier int	delcmd(char*);
457dd7cddfSDavid du Colombier int	helpcmd(char*);
467dd7cddfSDavid du Colombier int	listcmd(char*);
477dd7cddfSDavid du Colombier int	mdtmcmd(char*);
487dd7cddfSDavid du Colombier int	mkdircmd(char*);
497dd7cddfSDavid du Colombier int	modecmd(char*);
507dd7cddfSDavid du Colombier int	namelistcmd(char*);
517dd7cddfSDavid du Colombier int	nopcmd(char*);
527dd7cddfSDavid du Colombier int	passcmd(char*);
537dd7cddfSDavid du Colombier int	pasvcmd(char*);
547dd7cddfSDavid du Colombier int	portcmd(char*);
557dd7cddfSDavid du Colombier int	pwdcmd(char*);
567dd7cddfSDavid du Colombier int	quitcmd(char*);
577dd7cddfSDavid du Colombier int	rnfrcmd(char*);
587dd7cddfSDavid du Colombier int	rntocmd(char*);
597dd7cddfSDavid du Colombier int	reply(char*, ...);
607dd7cddfSDavid du Colombier int	restartcmd(char*);
617dd7cddfSDavid du Colombier int	retrievecmd(char*);
6207c70eb6SDavid du Colombier int	sitecmd(char*);
637dd7cddfSDavid du Colombier int	sizecmd(char*);
647dd7cddfSDavid du Colombier int	storecmd(char*);
657dd7cddfSDavid du Colombier int	storeucmd(char*);
667dd7cddfSDavid du Colombier int	structcmd(char*);
677dd7cddfSDavid du Colombier int	systemcmd(char*);
687dd7cddfSDavid du Colombier int	typecmd(char*);
697dd7cddfSDavid du Colombier int	usercmd(char*);
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier int	dialdata(void);
727dd7cddfSDavid du Colombier char*	abspath(char*);
737dd7cddfSDavid du Colombier int	crlfwrite(int, char*, int);
747dd7cddfSDavid du Colombier int	sodoff(void);
759a747e4fSDavid du Colombier int	accessok(char*);
767dd7cddfSDavid du Colombier 
777dd7cddfSDavid du Colombier typedef struct Cmd	Cmd;
787dd7cddfSDavid du Colombier struct Cmd
797dd7cddfSDavid du Colombier {
807dd7cddfSDavid du Colombier 	char	*name;
817dd7cddfSDavid du Colombier 	int	(*f)(char*);
827dd7cddfSDavid du Colombier 	int	needlogin;
837dd7cddfSDavid du Colombier };
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier Cmd cmdtab[] =
867dd7cddfSDavid du Colombier {
877dd7cddfSDavid du Colombier 	{ "abor",	abortcmd,	0, },
887dd7cddfSDavid du Colombier 	{ "appe",	appendcmd,	1, },
897dd7cddfSDavid du Colombier 	{ "cdup",	cdupcmd,	1, },
907dd7cddfSDavid du Colombier 	{ "cwd",	cwdcmd,		1, },
917dd7cddfSDavid du Colombier 	{ "dele",	delcmd,		1, },
927dd7cddfSDavid du Colombier 	{ "help",	helpcmd,	0, },
937dd7cddfSDavid du Colombier 	{ "list",	listcmd,	1, },
947dd7cddfSDavid du Colombier 	{ "mdtm",	mdtmcmd,	1, },
957dd7cddfSDavid du Colombier 	{ "mkd",	mkdircmd,	1, },
967dd7cddfSDavid du Colombier 	{ "mode",	modecmd,	0, },
977dd7cddfSDavid du Colombier 	{ "nlst",	namelistcmd,	1, },
987dd7cddfSDavid du Colombier 	{ "noop",	nopcmd,		0, },
997dd7cddfSDavid du Colombier 	{ "pass",	passcmd,	0, },
1007dd7cddfSDavid du Colombier 	{ "pasv",	pasvcmd,	1, },
1017dd7cddfSDavid du Colombier 	{ "pwd",	pwdcmd,		0, },
1027dd7cddfSDavid du Colombier 	{ "port", 	portcmd,	1, },
1037dd7cddfSDavid du Colombier 	{ "quit",	quitcmd,	0, },
1047dd7cddfSDavid du Colombier 	{ "rest",	restartcmd,	1, },
1057dd7cddfSDavid du Colombier 	{ "retr",	retrievecmd,	1, },
1069a747e4fSDavid du Colombier 	{ "rmd",	delcmd,		1, },
1077dd7cddfSDavid du Colombier 	{ "rnfr",	rnfrcmd,	1, },
1087dd7cddfSDavid du Colombier 	{ "rnto",	rntocmd,	1, },
10907c70eb6SDavid du Colombier 	{ "site", sitecmd, 1, },
1107dd7cddfSDavid du Colombier 	{ "size", 	sizecmd,	1, },
1117dd7cddfSDavid du Colombier 	{ "stor", 	storecmd,	1, },
1127dd7cddfSDavid du Colombier 	{ "stou", 	storeucmd,	1, },
1137dd7cddfSDavid du Colombier 	{ "stru",	structcmd,	1, },
1147dd7cddfSDavid du Colombier 	{ "syst",	systemcmd,	0, },
1157dd7cddfSDavid du Colombier 	{ "type", 	typecmd,	0, },
1167dd7cddfSDavid du Colombier 	{ "user",	usercmd,	0, },
1177dd7cddfSDavid du Colombier 	{ 0, 0, 0 },
1187dd7cddfSDavid du Colombier };
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier #define NONENS "/lib/namespace.ftp"	/* default ns for none */
1217dd7cddfSDavid du Colombier 
1229a747e4fSDavid du Colombier char	user[Maxpath];		/* logged in user */
1237dd7cddfSDavid du Colombier char	curdir[Maxpath];	/* current directory path */
1249a747e4fSDavid du Colombier Chalstate	*ch;
1257dd7cddfSDavid du Colombier int	loggedin;
1267dd7cddfSDavid du Colombier int	type;			/* transmission type */
1277dd7cddfSDavid du Colombier int	mode;			/* transmission mode */
1287dd7cddfSDavid du Colombier int	structure;		/* file structure */
1297dd7cddfSDavid du Colombier char	data[64];		/* data address */
1307dd7cddfSDavid du Colombier int	pid;			/* transfer process */
1317dd7cddfSDavid du Colombier int	encryption;		/* encryption state */
13212d764a1SDavid du Colombier int	isnone, anon_ok, anon_only, anon_everybody;
1339a747e4fSDavid du Colombier char	cputype[Maxpath];	/* the environment variable of the same name */
1349a747e4fSDavid du Colombier char	bindir[Maxpath];	/* bin directory for this architecture */
1359a747e4fSDavid du Colombier char	mailaddr[Maxpath];
1367dd7cddfSDavid du Colombier char	*namespace = NONENS;
1377dd7cddfSDavid du Colombier int	debug;
1389a747e4fSDavid du Colombier NetConnInfo	*nci;
1397dd7cddfSDavid du Colombier int	createperm = 0660;
1407dd7cddfSDavid du Colombier int	isnoworld;
1417dd7cddfSDavid du Colombier vlong	offset;			/* from restart command */
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier ulong id;
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier typedef struct Passive Passive;
1467dd7cddfSDavid du Colombier struct Passive
1477dd7cddfSDavid du Colombier {
1487dd7cddfSDavid du Colombier 	int	inuse;
1497dd7cddfSDavid du Colombier 	char	adir[40];
1507dd7cddfSDavid du Colombier 	int	afd;
1517dd7cddfSDavid du Colombier 	int	port;
1527dd7cddfSDavid du Colombier 	uchar	ipaddr[IPaddrlen];
1537dd7cddfSDavid du Colombier } passive;
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier #define FTPLOG "ftp"
1567dd7cddfSDavid du Colombier 
1577dd7cddfSDavid du Colombier void
logit(char * fmt,...)1587dd7cddfSDavid du Colombier logit(char *fmt, ...)
1597dd7cddfSDavid du Colombier {
1607dd7cddfSDavid du Colombier 	char buf[8192];
1617dd7cddfSDavid du Colombier 	va_list arg;
1629a747e4fSDavid du Colombier 	char errstr[128];
1637dd7cddfSDavid du Colombier 
1649a747e4fSDavid du Colombier 	rerrstr(errstr, sizeof errstr);
1657dd7cddfSDavid du Colombier 	va_start(arg, fmt);
1669a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
1677dd7cddfSDavid du Colombier 	va_end(arg);
1689a747e4fSDavid du Colombier 	syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf);
1699a747e4fSDavid du Colombier 	werrstr(errstr, sizeof errstr);
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
172b241b7c4SDavid du Colombier static void
usage(void)173b241b7c4SDavid du Colombier usage(void)
174b241b7c4SDavid du Colombier {
175b241b7c4SDavid du Colombier 	syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0);
176b241b7c4SDavid du Colombier 	fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0);
177b241b7c4SDavid du Colombier 	exits("usage");
178b241b7c4SDavid du Colombier }
179b241b7c4SDavid du Colombier 
1807dd7cddfSDavid du Colombier /*
1817dd7cddfSDavid du Colombier  *  read commands from the control stream and dispatch
1827dd7cddfSDavid du Colombier  */
1837dd7cddfSDavid du Colombier void
main(int argc,char ** argv)1847dd7cddfSDavid du Colombier main(int argc, char **argv)
1857dd7cddfSDavid du Colombier {
1867dd7cddfSDavid du Colombier 	char *cmd;
1877dd7cddfSDavid du Colombier 	char *arg;
1887dd7cddfSDavid du Colombier 	char *p;
1897dd7cddfSDavid du Colombier 	Cmd *t;
1907dd7cddfSDavid du Colombier 	Biobuf in;
1917dd7cddfSDavid du Colombier 	int i;
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 	ARGBEGIN{
194d9306527SDavid du Colombier 	case 'a':		/* anonymous OK */
195d9306527SDavid du Colombier 		anon_ok = 1;
196d9306527SDavid du Colombier 		break;
197d9306527SDavid du Colombier 	case 'A':
198d9306527SDavid du Colombier 		anon_ok = 1;
199d9306527SDavid du Colombier 		anon_only = 1;
2007dd7cddfSDavid du Colombier 		break;
201b241b7c4SDavid du Colombier 	case 'd':
202b241b7c4SDavid du Colombier 		debug++;
203b241b7c4SDavid du Colombier 		break;
20412d764a1SDavid du Colombier 	case 'e':
20512d764a1SDavid du Colombier 		anon_ok = 1;
20612d764a1SDavid du Colombier 		anon_everybody = 1;
20712d764a1SDavid du Colombier 		break;
2087dd7cddfSDavid du Colombier 	case 'n':
209b241b7c4SDavid du Colombier 		namespace = EARGF(usage());
2107dd7cddfSDavid du Colombier 		break;
211b241b7c4SDavid du Colombier 	default:
212b241b7c4SDavid du Colombier 		usage();
2137dd7cddfSDavid du Colombier 	}ARGEND
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier 	/* open log file before doing a newns */
2167dd7cddfSDavid du Colombier 	syslog(0, FTPLOG, nil);
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier 	/* find out who is calling */
2199a747e4fSDavid du Colombier 	if(argc < 1)
2209a747e4fSDavid du Colombier 		nci = getnetconninfo(nil, 0);
2217dd7cddfSDavid du Colombier 	else
2229a747e4fSDavid du Colombier 		nci = getnetconninfo(argv[argc-1], 0);
2239a747e4fSDavid du Colombier 	if(nci == nil)
2249a747e4fSDavid du Colombier 		sysfatal("ftpd needs a network address");
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier 	strcpy(mailaddr, "?");
2277dd7cddfSDavid du Colombier 	id = getpid();
2287dd7cddfSDavid du Colombier 
229b241b7c4SDavid du Colombier 	/* figure out which binaries to bind in later (only for none) */
2307dd7cddfSDavid du Colombier 	arg = getenv("cputype");
2317dd7cddfSDavid du Colombier 	if(arg)
2324d44ba9bSDavid du Colombier 		strecpy(cputype, cputype+sizeof cputype, arg);
2337dd7cddfSDavid du Colombier 	else
2347dd7cddfSDavid du Colombier 		strcpy(cputype, "mips");
235b241b7c4SDavid du Colombier 	/* shurely /%s/bin */
2367dd7cddfSDavid du Colombier 	snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype);
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	Binit(&in, 0, OREAD);
2397dd7cddfSDavid du Colombier 	reply("220 Plan 9 FTP server ready");
2407dd7cddfSDavid du Colombier 	alarm(Maxwait);
2417dd7cddfSDavid du Colombier 	while(cmd = Brdline(&in, '\n')){
2427dd7cddfSDavid du Colombier 		alarm(0);
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier 		/*
2457dd7cddfSDavid du Colombier 		 *  strip out trailing cr's & lf and delimit with null
2467dd7cddfSDavid du Colombier 		 */
2477dd7cddfSDavid du Colombier 		i = Blinelen(&in)-1;
2487dd7cddfSDavid du Colombier 		cmd[i] = 0;
2497dd7cddfSDavid du Colombier 		if(debug)
2507dd7cddfSDavid du Colombier 			logit("%s", cmd);
2517dd7cddfSDavid du Colombier 		while(i > 0 && cmd[i-1] == '\r')
2527dd7cddfSDavid du Colombier 			cmd[--i] = 0;
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 		/*
2557dd7cddfSDavid du Colombier 		 *  hack for GatorFTP+, look for a 0x10 used as a delimiter
2567dd7cddfSDavid du Colombier 		 */
2577dd7cddfSDavid du Colombier 		p = strchr(cmd, 0x10);
2587dd7cddfSDavid du Colombier 		if(p)
2597dd7cddfSDavid du Colombier 			*p = 0;
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 		/*
2627dd7cddfSDavid du Colombier 		 *  get rid of telnet control sequences (we don't need them)
2637dd7cddfSDavid du Colombier 		 */
26422a127bbSDavid du Colombier 		while(*cmd && (uchar)*cmd == Iac){
2657dd7cddfSDavid du Colombier 			cmd++;
2667dd7cddfSDavid du Colombier 			if(*cmd)
2677dd7cddfSDavid du Colombier 				cmd++;
2687dd7cddfSDavid du Colombier 		}
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier 		/*
2717dd7cddfSDavid du Colombier 		 *  parse the message (command arg)
2727dd7cddfSDavid du Colombier 		 */
2737dd7cddfSDavid du Colombier 		arg = strchr(cmd, ' ');
2747dd7cddfSDavid du Colombier 		if(arg){
2757dd7cddfSDavid du Colombier 			*arg++ = 0;
2767dd7cddfSDavid du Colombier 			while(*arg == ' ')
2777dd7cddfSDavid du Colombier 				arg++;
2787dd7cddfSDavid du Colombier 		}
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 		/*
2817dd7cddfSDavid du Colombier 		 *  ignore blank commands
2827dd7cddfSDavid du Colombier 		 */
2837dd7cddfSDavid du Colombier 		if(*cmd == 0)
2847dd7cddfSDavid du Colombier 			continue;
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 		/*
2877dd7cddfSDavid du Colombier 		 *  lookup the command and do it
2887dd7cddfSDavid du Colombier 		 */
2897dd7cddfSDavid du Colombier 		for(p = cmd; *p; p++)
2907dd7cddfSDavid du Colombier 			*p = tolower(*p);
2917dd7cddfSDavid du Colombier 		for(t = cmdtab; t->name; t++)
2927dd7cddfSDavid du Colombier 			if(strcmp(cmd, t->name) == 0){
2937dd7cddfSDavid du Colombier 				if(t->needlogin && !loggedin)
2947dd7cddfSDavid du Colombier 					sodoff();
2957dd7cddfSDavid du Colombier 				else if((*t->f)(arg) < 0)
2967dd7cddfSDavid du Colombier 					exits(0);
2977dd7cddfSDavid du Colombier 				break;
2987dd7cddfSDavid du Colombier 			}
2999a747e4fSDavid du Colombier 		if(t->f != restartcmd){
3007dd7cddfSDavid du Colombier 			/*
3017dd7cddfSDavid du Colombier 			 *  the file offset is set to zero following
3027dd7cddfSDavid du Colombier 			 *  all commands except the restart command
3037dd7cddfSDavid du Colombier 			 */
3047dd7cddfSDavid du Colombier 			offset = 0;
3057dd7cddfSDavid du Colombier 		}
3067dd7cddfSDavid du Colombier 		if(t->name == 0){
3077dd7cddfSDavid du Colombier 			/*
3087dd7cddfSDavid du Colombier 			 *  the OOB bytes preceding an abort from UCB machines
3097dd7cddfSDavid du Colombier 			 *  comes out as something unrecognizable instead of
3107dd7cddfSDavid du Colombier 			 *  IAC's.  Certainly a Plan 9 bug but I can't find it.
3117dd7cddfSDavid du Colombier 			 *  This is a major hack to avoid the problem. -- presotto
3127dd7cddfSDavid du Colombier 			 */
3137dd7cddfSDavid du Colombier 			i = strlen(cmd);
3147dd7cddfSDavid du Colombier 			if(i > 4 && strcmp(cmd+i-4, "abor") == 0){
3157dd7cddfSDavid du Colombier 				abortcmd(0);
3167dd7cddfSDavid du Colombier 			} else{
3177dd7cddfSDavid du Colombier 				logit("%s (%s) command not implemented", cmd, arg?arg:"");
3187dd7cddfSDavid du Colombier 				reply("502 %s command not implemented", cmd);
3197dd7cddfSDavid du Colombier 			}
3207dd7cddfSDavid du Colombier 		}
3217dd7cddfSDavid du Colombier 		alarm(Maxwait);
3227dd7cddfSDavid du Colombier 	}
3237dd7cddfSDavid du Colombier 	if(pid)
324b27b55e2SDavid du Colombier 		postnote(PNPROC, pid, "kill");
3257dd7cddfSDavid du Colombier }
3267dd7cddfSDavid du Colombier 
3277dd7cddfSDavid du Colombier /*
3287dd7cddfSDavid du Colombier  *  reply to a command
3297dd7cddfSDavid du Colombier  */
3307dd7cddfSDavid du Colombier int
reply(char * fmt,...)3317dd7cddfSDavid du Colombier reply(char *fmt, ...)
3327dd7cddfSDavid du Colombier {
3337dd7cddfSDavid du Colombier 	va_list arg;
3347dd7cddfSDavid du Colombier 	char buf[8192], *s;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 	va_start(arg, fmt);
3376b6b9ac8SDavid du Colombier 	s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg);
3387dd7cddfSDavid du Colombier 	va_end(arg);
3397dd7cddfSDavid du Colombier 	if(debug){
3407dd7cddfSDavid du Colombier 		*s = 0;
3417dd7cddfSDavid du Colombier 		logit("%s", buf);
3427dd7cddfSDavid du Colombier 	}
3437dd7cddfSDavid du Colombier 	*s++ = '\r';
3447dd7cddfSDavid du Colombier 	*s++ = '\n';
3457dd7cddfSDavid du Colombier 	write(1, buf, s - buf);
3467dd7cddfSDavid du Colombier 	return 0;
3477dd7cddfSDavid du Colombier }
3487dd7cddfSDavid du Colombier 
3497dd7cddfSDavid du Colombier int
sodoff(void)3507dd7cddfSDavid du Colombier sodoff(void)
3517dd7cddfSDavid du Colombier {
3527dd7cddfSDavid du Colombier 	return reply("530 Sod off, service requires login");
3537dd7cddfSDavid du Colombier }
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier /*
3567dd7cddfSDavid du Colombier  *  run a command in a separate process
3577dd7cddfSDavid du Colombier  */
3587dd7cddfSDavid du Colombier int
asproc(void (* f)(char *,int),char * arg,int arg2)3597dd7cddfSDavid du Colombier asproc(void (*f)(char*, int), char *arg, int arg2)
3607dd7cddfSDavid du Colombier {
3617dd7cddfSDavid du Colombier 	int i;
3627dd7cddfSDavid du Colombier 
3637dd7cddfSDavid du Colombier 	if(pid){
3647dd7cddfSDavid du Colombier 		/* wait for previous command to finish */
3657dd7cddfSDavid du Colombier 		for(;;){
3669a747e4fSDavid du Colombier 			i = waitpid();
3677dd7cddfSDavid du Colombier 			if(i == pid || i < 0)
3687dd7cddfSDavid du Colombier 				break;
3697dd7cddfSDavid du Colombier 		}
3707dd7cddfSDavid du Colombier 	}
3717dd7cddfSDavid du Colombier 
3727dd7cddfSDavid du Colombier 	switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
3737dd7cddfSDavid du Colombier 	case -1:
3747dd7cddfSDavid du Colombier 		return reply("450 Out of processes: %r");
3757dd7cddfSDavid du Colombier 	case 0:
3767dd7cddfSDavid du Colombier 		(*f)(arg, arg2);
3777dd7cddfSDavid du Colombier 		exits(0);
3787dd7cddfSDavid du Colombier 	default:
3797dd7cddfSDavid du Colombier 		break;
3807dd7cddfSDavid du Colombier 	}
3817dd7cddfSDavid du Colombier 	return 0;
3827dd7cddfSDavid du Colombier }
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier /*
3857dd7cddfSDavid du Colombier  * run a command to filter a tail
3867dd7cddfSDavid du Colombier  */
3877dd7cddfSDavid du Colombier int
transfer(char * cmd,char * a1,char * a2,char * a3,int image)3887dd7cddfSDavid du Colombier transfer(char *cmd, char *a1, char *a2, char *a3, int image)
3897dd7cddfSDavid du Colombier {
3909a747e4fSDavid du Colombier 	int n, dfd, fd, bytes, eofs, pid;
3917dd7cddfSDavid du Colombier 	int pfd[2];
3927dd7cddfSDavid du Colombier 	char buf[Nbuf], *p;
3939a747e4fSDavid du Colombier 	Waitmsg *w;
3947dd7cddfSDavid du Colombier 
3957dd7cddfSDavid du Colombier 	reply("150 Opening data connection for %s (%s)", cmd, data);
3967dd7cddfSDavid du Colombier 	dfd = dialdata();
3977dd7cddfSDavid du Colombier 	if(dfd < 0)
3987dd7cddfSDavid du Colombier 		return reply("425 Error opening data connection: %r");
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier 	if(pipe(pfd) < 0)
4017dd7cddfSDavid du Colombier 		return reply("520 Internal Error: %r");
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier 	bytes = 0;
4047dd7cddfSDavid du Colombier 	switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){
4057dd7cddfSDavid du Colombier 	case -1:
4067dd7cddfSDavid du Colombier 		return reply("450 Out of processes: %r");
4077dd7cddfSDavid du Colombier 	case 0:
408b27b55e2SDavid du Colombier 		logit("running %s %s %s %s pid %d",
409b27b55e2SDavid du Colombier 			cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid());
4107dd7cddfSDavid du Colombier 		close(pfd[1]);
4117dd7cddfSDavid du Colombier 		close(dfd);
4127dd7cddfSDavid du Colombier 		dup(pfd[0], 1);
4137dd7cddfSDavid du Colombier 		dup(pfd[0], 2);
4147dd7cddfSDavid du Colombier 		if(isnone){
4157dd7cddfSDavid du Colombier 			fd = open("#s/boot", ORDWR);
4167dd7cddfSDavid du Colombier 			if(fd < 0
4177dd7cddfSDavid du Colombier 			|| bind("#/", "/", MAFTER) < 0
4189a747e4fSDavid du Colombier 			|| amount(fd, "/bin", MREPL, "") < 0
4197dd7cddfSDavid du Colombier 			|| bind("#c", "/dev", MAFTER) < 0
4207dd7cddfSDavid du Colombier 			|| bind(bindir, "/bin", MREPL) < 0)
4217dd7cddfSDavid du Colombier 				exits("building name space");
4227dd7cddfSDavid du Colombier 			close(fd);
4237dd7cddfSDavid du Colombier 		}
424f19e7b74SDavid du Colombier 		execl(cmd, cmd, a1, a2, a3, nil);
4257dd7cddfSDavid du Colombier 		exits(cmd);
4267dd7cddfSDavid du Colombier 	default:
4277dd7cddfSDavid du Colombier 		close(pfd[0]);
4287dd7cddfSDavid du Colombier 		eofs = 0;
4297dd7cddfSDavid du Colombier 		while((n = read(pfd[1], buf, sizeof buf)) >= 0){
4307dd7cddfSDavid du Colombier 			if(n == 0){
4317dd7cddfSDavid du Colombier 				if(eofs++ > 5)
4327dd7cddfSDavid du Colombier 					break;
4337dd7cddfSDavid du Colombier 				else
4347dd7cddfSDavid du Colombier 					continue;
4357dd7cddfSDavid du Colombier 			}
4367dd7cddfSDavid du Colombier 			eofs = 0;
4377dd7cddfSDavid du Colombier 			p = buf;
4387dd7cddfSDavid du Colombier 			if(offset > 0){
4397dd7cddfSDavid du Colombier 				if(n > offset){
4407dd7cddfSDavid du Colombier 					p = buf+offset;
4417dd7cddfSDavid du Colombier 					n -= offset;
4427dd7cddfSDavid du Colombier 					offset = 0;
4437dd7cddfSDavid du Colombier 				} else {
4447dd7cddfSDavid du Colombier 					offset -= n;
4457dd7cddfSDavid du Colombier 					continue;
4467dd7cddfSDavid du Colombier 				}
4477dd7cddfSDavid du Colombier 			}
4487dd7cddfSDavid du Colombier 			if(!image)
4497dd7cddfSDavid du Colombier 				n = crlfwrite(dfd, p, n);
4507dd7cddfSDavid du Colombier 			else
4517dd7cddfSDavid du Colombier 				n = write(dfd, p, n);
4527dd7cddfSDavid du Colombier 			if(n < 0){
4537dd7cddfSDavid du Colombier 				postnote(PNPROC, pid, "kill");
4547dd7cddfSDavid du Colombier 				bytes = -1;
4557dd7cddfSDavid du Colombier 				break;
4567dd7cddfSDavid du Colombier 			}
4577dd7cddfSDavid du Colombier 			bytes += n;
4587dd7cddfSDavid du Colombier 		}
4599a747e4fSDavid du Colombier 		close(pfd[1]);
4607dd7cddfSDavid du Colombier 		close(dfd);
4617dd7cddfSDavid du Colombier 		break;
4627dd7cddfSDavid du Colombier 	}
4637dd7cddfSDavid du Colombier 
4647dd7cddfSDavid du Colombier 	/* wait for this command to finish */
4657dd7cddfSDavid du Colombier 	for(;;){
4669a747e4fSDavid du Colombier 		w = wait();
4679a747e4fSDavid du Colombier 		if(w == nil || w->pid == pid)
4687dd7cddfSDavid du Colombier 			break;
4699a747e4fSDavid du Colombier 		free(w);
4707dd7cddfSDavid du Colombier 	}
471b27b55e2SDavid du Colombier 	if(w != nil && w->msg != nil && w->msg[0] != 0){
4727dd7cddfSDavid du Colombier 		bytes = -1;
4739a747e4fSDavid du Colombier 		logit("%s", w->msg);
4749a747e4fSDavid du Colombier 		logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg);
4757dd7cddfSDavid du Colombier 	}
4769a747e4fSDavid du Colombier 	free(w);
4777dd7cddfSDavid du Colombier 	reply("226 Transfer complete");
4787dd7cddfSDavid du Colombier 	return bytes;
4797dd7cddfSDavid du Colombier }
4807dd7cddfSDavid du Colombier 
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier /*
4837dd7cddfSDavid du Colombier  *  just reply OK
4847dd7cddfSDavid du Colombier  */
4857dd7cddfSDavid du Colombier int
nopcmd(char * arg)4867dd7cddfSDavid du Colombier nopcmd(char *arg)
4877dd7cddfSDavid du Colombier {
4887dd7cddfSDavid du Colombier 	USED(arg);
4897dd7cddfSDavid du Colombier 	reply("510 Plan 9 FTP daemon still alive");
4907dd7cddfSDavid du Colombier 	return 0;
4917dd7cddfSDavid du Colombier }
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier /*
4947dd7cddfSDavid du Colombier  *  login as user
4957dd7cddfSDavid du Colombier  */
4967dd7cddfSDavid du Colombier int
loginuser(char * user,char * nsfile,int gotoslash)4977dd7cddfSDavid du Colombier loginuser(char *user, char *nsfile, int gotoslash)
4987dd7cddfSDavid du Colombier {
4999a747e4fSDavid du Colombier 	logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile);
5007dd7cddfSDavid du Colombier 	if(nsfile != nil && newns(user, nsfile) < 0){
5017dd7cddfSDavid du Colombier 		logit("namespace file %s does not exist", nsfile);
5027dd7cddfSDavid du Colombier 		return reply("530 Not logged in: login out of service");
5037dd7cddfSDavid du Colombier 	}
5047dd7cddfSDavid du Colombier 	getwd(curdir, sizeof(curdir));
5057dd7cddfSDavid du Colombier 	if(gotoslash){
5067dd7cddfSDavid du Colombier 		chdir("/");
5077dd7cddfSDavid du Colombier 		strcpy(curdir, "/");
5087dd7cddfSDavid du Colombier 	}
5097dd7cddfSDavid du Colombier 	putenv("service", "ftp");
5107dd7cddfSDavid du Colombier 	loggedin = 1;
5117dd7cddfSDavid du Colombier 	if(debug == 0)
5127dd7cddfSDavid du Colombier 		reply("230- If you have problems, send mail to 'postmaster'.");
5137dd7cddfSDavid du Colombier 	return reply("230 Logged in");
5147dd7cddfSDavid du Colombier }
5157dd7cddfSDavid du Colombier 
51688a5d610SDavid du Colombier static void
slowdown(void)51788a5d610SDavid du Colombier slowdown(void)
51888a5d610SDavid du Colombier {
51988a5d610SDavid du Colombier 	static ulong pause;
52088a5d610SDavid du Colombier 
52188a5d610SDavid du Colombier 	if (pause) {
52288a5d610SDavid du Colombier 		sleep(pause);			/* deter guessers */
52388a5d610SDavid du Colombier 		if (pause < (1UL << 20))
52488a5d610SDavid du Colombier 			pause *= 2;
52588a5d610SDavid du Colombier 	} else
52688a5d610SDavid du Colombier 		pause = 1000;
52788a5d610SDavid du Colombier }
52888a5d610SDavid du Colombier 
5297dd7cddfSDavid du Colombier /*
5307dd7cddfSDavid du Colombier  *  get a user id, reply with a challenge.  The users 'anonymous'
5317dd7cddfSDavid du Colombier  *  and 'ftp' are equivalent to 'none'.  The user 'none' requires
5327dd7cddfSDavid du Colombier  *  no challenge.
5337dd7cddfSDavid du Colombier  */
5347dd7cddfSDavid du Colombier int
usercmd(char * name)5357dd7cddfSDavid du Colombier usercmd(char *name)
5367dd7cddfSDavid du Colombier {
53788a5d610SDavid du Colombier 	slowdown();
53888a5d610SDavid du Colombier 
5399a747e4fSDavid du Colombier 	logit("user %s %s", name, nci->rsys);
5407dd7cddfSDavid du Colombier 	if(loggedin)
5417dd7cddfSDavid du Colombier 		return reply("530 Already logged in as %s", user);
5427dd7cddfSDavid du Colombier 	if(name == 0 || *name == 0)
5437dd7cddfSDavid du Colombier 		return reply("530 user command needs user name");
5447dd7cddfSDavid du Colombier 	isnoworld = 0;
5457dd7cddfSDavid du Colombier 	if(*name == ':'){
5467dd7cddfSDavid du Colombier 		debug = 1;
5477dd7cddfSDavid du Colombier 		name++;
5487dd7cddfSDavid du Colombier 	}
5497dd7cddfSDavid du Colombier 	strncpy(user, name, sizeof(user));
5507dd7cddfSDavid du Colombier 	if(debug)
5517dd7cddfSDavid du Colombier 		logit("debugging");
5527dd7cddfSDavid du Colombier 	user[sizeof(user)-1] = 0;
5537dd7cddfSDavid du Colombier 	if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0)
5547dd7cddfSDavid du Colombier 		strcpy(user, "none");
55512d764a1SDavid du Colombier 	else if(anon_everybody)
55612d764a1SDavid du Colombier 		strcpy(user,"none");
55788a5d610SDavid du Colombier 
55888a5d610SDavid du Colombier 	if(strcmp(user, "Administrator") == 0 || strcmp(user, "admin") == 0)
55988a5d610SDavid du Colombier 		return reply("530 go away, script kiddie");
56088a5d610SDavid du Colombier 	else if(strcmp(user, "*none") == 0){
561d9306527SDavid du Colombier 		if(!anon_ok)
5627dd7cddfSDavid du Colombier 			return reply("530 Not logged in: anonymous disallowed");
5637dd7cddfSDavid du Colombier 		return loginuser("none", namespace, 1);
5647dd7cddfSDavid du Colombier 	}
56588a5d610SDavid du Colombier 	else if(strcmp(user, "none") == 0){
566d9306527SDavid du Colombier 		if(!anon_ok)
5677dd7cddfSDavid du Colombier 			return reply("530 Not logged in: anonymous disallowed");
5687dd7cddfSDavid du Colombier 		return reply("331 Send email address as password");
5697dd7cddfSDavid du Colombier 	}
57088a5d610SDavid du Colombier 	else if(anon_only)
571d9306527SDavid du Colombier 		return reply("530 Not logged in: anonymous access only");
57288a5d610SDavid du Colombier 
5737dd7cddfSDavid du Colombier 	isnoworld = noworld(name);
5747dd7cddfSDavid du Colombier 	if(isnoworld)
5757dd7cddfSDavid du Colombier 		return reply("331 OK");
57688a5d610SDavid du Colombier 
57788a5d610SDavid du Colombier 	/* consult the auth server */
5789a747e4fSDavid du Colombier 	if(ch)
5799a747e4fSDavid du Colombier 		auth_freechal(ch);
5809a747e4fSDavid du Colombier 	if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
5817dd7cddfSDavid du Colombier 		return reply("421 %r");
5829a747e4fSDavid du Colombier 	return reply("331 encrypt challenge, %s, as a password", ch->chal);
5837dd7cddfSDavid du Colombier }
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier /*
5867dd7cddfSDavid du Colombier  *  get a password, set up user if it works.
5877dd7cddfSDavid du Colombier  */
5887dd7cddfSDavid du Colombier int
passcmd(char * response)5897dd7cddfSDavid du Colombier passcmd(char *response)
5907dd7cddfSDavid du Colombier {
5917dd7cddfSDavid du Colombier 	char namefile[128];
5929a747e4fSDavid du Colombier 	AuthInfo *ai;
5937dd7cddfSDavid du Colombier 
5947dd7cddfSDavid du Colombier 	if(response == nil)
5957dd7cddfSDavid du Colombier 		response = "";
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier 	if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){
5987dd7cddfSDavid du Colombier 		/* for none, accept anything as a password */
5997dd7cddfSDavid du Colombier 		isnone = 1;
6007dd7cddfSDavid du Colombier 		strncpy(mailaddr, response, sizeof(mailaddr)-1);
6017dd7cddfSDavid du Colombier 		return loginuser("none", namespace, 1);
6027dd7cddfSDavid du Colombier 	}
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier 	if(isnoworld){
6057dd7cddfSDavid du Colombier 		/* noworld gets a password in the clear */
6067dd7cddfSDavid du Colombier 		if(login(user, response, "/lib/namespace.noworld") < 0)
6077dd7cddfSDavid du Colombier 			return reply("530 Not logged in");
6087dd7cddfSDavid du Colombier 		createperm = 0664;
6097dd7cddfSDavid du Colombier 		/* login has already setup the namespace */
6107dd7cddfSDavid du Colombier 		return loginuser(user, nil, 0);
6117dd7cddfSDavid du Colombier 	} else {
6127dd7cddfSDavid du Colombier 		/* for everyone else, do challenge response */
6139a747e4fSDavid du Colombier 		if(ch == nil)
6147dd7cddfSDavid du Colombier 			return reply("531 Send user id before encrypted challenge");
6159a747e4fSDavid du Colombier 		ch->resp = response;
6169a747e4fSDavid du Colombier 		ch->nresp = strlen(response);
6179a747e4fSDavid du Colombier 		ai = auth_response(ch);
61888a5d610SDavid du Colombier 		if(ai == nil || auth_chuid(ai, nil) < 0) {
61988a5d610SDavid du Colombier 			slowdown();
6207dd7cddfSDavid du Colombier 			return reply("530 Not logged in: %r");
62184dd69a3SDavid du Colombier 		}
6229a747e4fSDavid du Colombier 		auth_freechal(ch);
6239a747e4fSDavid du Colombier 		ch = nil;
6247dd7cddfSDavid du Colombier 
6257dd7cddfSDavid du Colombier 		/* if the user has specified a namespace for ftp, use it */
6267dd7cddfSDavid du Colombier 		snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user);
6277dd7cddfSDavid du Colombier 		strcpy(mailaddr, user);
6287dd7cddfSDavid du Colombier 		createperm = 0660;
6299a747e4fSDavid du Colombier 		if(access(namefile, 0) == 0)
6307dd7cddfSDavid du Colombier 			return loginuser(user, namefile, 0);
6317dd7cddfSDavid du Colombier 		else
6327dd7cddfSDavid du Colombier 			return loginuser(user, "/lib/namespace", 0);
6337dd7cddfSDavid du Colombier 	}
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier /*
6377dd7cddfSDavid du Colombier  *  print working directory
6387dd7cddfSDavid du Colombier  */
6397dd7cddfSDavid du Colombier int
pwdcmd(char * arg)6407dd7cddfSDavid du Colombier pwdcmd(char *arg)
6417dd7cddfSDavid du Colombier {
6427dd7cddfSDavid du Colombier 	if(arg)
6437dd7cddfSDavid du Colombier 		return reply("550 Pwd takes no argument");
6447dd7cddfSDavid du Colombier 	return reply("257 \"%s\" is the current directory", curdir);
6457dd7cddfSDavid du Colombier }
6467dd7cddfSDavid du Colombier 
6477dd7cddfSDavid du Colombier /*
6487dd7cddfSDavid du Colombier  *  chdir
6497dd7cddfSDavid du Colombier  */
6507dd7cddfSDavid du Colombier int
cwdcmd(char * dir)6517dd7cddfSDavid du Colombier cwdcmd(char *dir)
6527dd7cddfSDavid du Colombier {
6537dd7cddfSDavid du Colombier 	char *rp;
6549a747e4fSDavid du Colombier 	char buf[Maxpath];
6557dd7cddfSDavid du Colombier 
6567dd7cddfSDavid du Colombier 	/* shell cd semantics */
6577dd7cddfSDavid du Colombier 	if(dir == 0 || *dir == 0){
6587dd7cddfSDavid du Colombier 		if(isnone)
6597dd7cddfSDavid du Colombier 			rp = "/";
6607dd7cddfSDavid du Colombier 		else {
6617dd7cddfSDavid du Colombier 			snprint(buf, sizeof buf, "/usr/%s", user);
6627dd7cddfSDavid du Colombier 			rp = buf;
6637dd7cddfSDavid du Colombier 		}
6649a747e4fSDavid du Colombier 		if(accessok(rp) == 0)
6659a747e4fSDavid du Colombier 			rp = nil;
6667dd7cddfSDavid du Colombier 	} else
6677dd7cddfSDavid du Colombier 		rp = abspath(dir);
6687dd7cddfSDavid du Colombier 
6699a747e4fSDavid du Colombier 	if(rp == nil)
6709a747e4fSDavid du Colombier 		return reply("550 Permission denied");
6719a747e4fSDavid du Colombier 
6727dd7cddfSDavid du Colombier 	if(chdir(rp) < 0)
6737dd7cddfSDavid du Colombier 		return reply("550 Cwd failed: %r");
6747dd7cddfSDavid du Colombier 	strcpy(curdir, rp);
6757dd7cddfSDavid du Colombier 	return reply("250 directory changed to %s", curdir);
6767dd7cddfSDavid du Colombier }
6777dd7cddfSDavid du Colombier 
6787dd7cddfSDavid du Colombier /*
6797dd7cddfSDavid du Colombier  *  chdir ..
6807dd7cddfSDavid du Colombier  */
6817dd7cddfSDavid du Colombier int
cdupcmd(char * dp)6827dd7cddfSDavid du Colombier cdupcmd(char *dp)
6837dd7cddfSDavid du Colombier {
6847dd7cddfSDavid du Colombier 	USED(dp);
6857dd7cddfSDavid du Colombier 	return cwdcmd("..");
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier int
quitcmd(char * arg)6897dd7cddfSDavid du Colombier quitcmd(char *arg)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier 	USED(arg);
6927dd7cddfSDavid du Colombier 	reply("200 Bye");
6937dd7cddfSDavid du Colombier 	if(pid)
694b27b55e2SDavid du Colombier 		postnote(PNPROC, pid, "kill");
6957dd7cddfSDavid du Colombier 	return -1;
6967dd7cddfSDavid du Colombier }
6977dd7cddfSDavid du Colombier 
6987dd7cddfSDavid du Colombier int
typecmd(char * arg)6997dd7cddfSDavid du Colombier typecmd(char *arg)
7007dd7cddfSDavid du Colombier {
7017dd7cddfSDavid du Colombier 	int c;
7027dd7cddfSDavid du Colombier 	char *x;
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier 	x = arg;
7057dd7cddfSDavid du Colombier 	if(arg == 0)
7067dd7cddfSDavid du Colombier 		return reply("501 Type command needs arguments");
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier 	while(c = *arg++){
7097dd7cddfSDavid du Colombier 		switch(tolower(c)){
7107dd7cddfSDavid du Colombier 		case 'a':
7117dd7cddfSDavid du Colombier 			type = Tascii;
7127dd7cddfSDavid du Colombier 			break;
7137dd7cddfSDavid du Colombier 		case 'i':
7147dd7cddfSDavid du Colombier 		case 'l':
7157dd7cddfSDavid du Colombier 			type = Timage;
7167dd7cddfSDavid du Colombier 			break;
7177dd7cddfSDavid du Colombier 		case '8':
7187dd7cddfSDavid du Colombier 		case ' ':
7197dd7cddfSDavid du Colombier 		case 'n':
7207dd7cddfSDavid du Colombier 		case 't':
7217dd7cddfSDavid du Colombier 		case 'c':
7227dd7cddfSDavid du Colombier 			break;
7237dd7cddfSDavid du Colombier 		default:
7247dd7cddfSDavid du Colombier 			return reply("501 Unimplemented type %s", x);
7257dd7cddfSDavid du Colombier 		}
7267dd7cddfSDavid du Colombier 	}
7277dd7cddfSDavid du Colombier 	return reply("200 Type %s", type==Tascii ? "Ascii" : "Image");
7287dd7cddfSDavid du Colombier }
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier int
modecmd(char * arg)7317dd7cddfSDavid du Colombier modecmd(char *arg)
7327dd7cddfSDavid du Colombier {
7337dd7cddfSDavid du Colombier 	if(arg == 0)
7347dd7cddfSDavid du Colombier 		return reply("501 Mode command needs arguments");
7357dd7cddfSDavid du Colombier 	while(*arg){
7367dd7cddfSDavid du Colombier 		switch(tolower(*arg)){
7377dd7cddfSDavid du Colombier 		case 's':
7387dd7cddfSDavid du Colombier 			mode = Mstream;
7397dd7cddfSDavid du Colombier 			break;
7407dd7cddfSDavid du Colombier 		default:
7417dd7cddfSDavid du Colombier 			return reply("501 Unimplemented mode %c", *arg);
7427dd7cddfSDavid du Colombier 		}
7437dd7cddfSDavid du Colombier 		arg++;
7447dd7cddfSDavid du Colombier 	}
7457dd7cddfSDavid du Colombier 	return reply("200 Stream mode");
7467dd7cddfSDavid du Colombier }
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier int
structcmd(char * arg)7497dd7cddfSDavid du Colombier structcmd(char *arg)
7507dd7cddfSDavid du Colombier {
7517dd7cddfSDavid du Colombier 	if(arg == 0)
7527dd7cddfSDavid du Colombier 		return reply("501 Struct command needs arguments");
7537dd7cddfSDavid du Colombier 	for(; *arg; arg++){
7547dd7cddfSDavid du Colombier 		switch(tolower(*arg)){
7557dd7cddfSDavid du Colombier 		case 'f':
7567dd7cddfSDavid du Colombier 			structure = Sfile;
7577dd7cddfSDavid du Colombier 			break;
7587dd7cddfSDavid du Colombier 		default:
7597dd7cddfSDavid du Colombier 			return reply("501 Unimplemented structure %c", *arg);
7607dd7cddfSDavid du Colombier 		}
7617dd7cddfSDavid du Colombier 	}
7627dd7cddfSDavid du Colombier 	return reply("200 File structure");
7637dd7cddfSDavid du Colombier }
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier int
portcmd(char * arg)7667dd7cddfSDavid du Colombier portcmd(char *arg)
7677dd7cddfSDavid du Colombier {
7687dd7cddfSDavid du Colombier 	char *field[7];
7697dd7cddfSDavid du Colombier 	int n;
7707dd7cddfSDavid du Colombier 
7717dd7cddfSDavid du Colombier 	if(arg == 0)
7727dd7cddfSDavid du Colombier 		return reply("501 Port command needs arguments");
7737dd7cddfSDavid du Colombier 	n = getfields(arg, field, 7, 0, ", ");
7747dd7cddfSDavid du Colombier 	if(n != 6)
7757dd7cddfSDavid du Colombier 		return reply("501 Incorrect port specification");
77659cc4ca5SDavid du Colombier 	snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2],
7777dd7cddfSDavid du Colombier 		field[3], atoi(field[4])*256 + atoi(field[5]));
7787dd7cddfSDavid du Colombier 	return reply("200 Data port is %s", data);
7797dd7cddfSDavid du Colombier }
7807dd7cddfSDavid du Colombier 
7817dd7cddfSDavid du Colombier int
mountnet(void)7827dd7cddfSDavid du Colombier mountnet(void)
7837dd7cddfSDavid du Colombier {
7849a747e4fSDavid du Colombier 	int rv;
7859a747e4fSDavid du Colombier 
7869a747e4fSDavid du Colombier 	rv = 0;
7879a747e4fSDavid du Colombier 
7887dd7cddfSDavid du Colombier 	if(bind("#/", "/", MAFTER) < 0){
7897dd7cddfSDavid du Colombier 		logit("can't bind #/ to /: %r");
7907dd7cddfSDavid du Colombier 		return reply("500 can't bind #/ to /: %r");
7917dd7cddfSDavid du Colombier 	}
7927dd7cddfSDavid du Colombier 
7939a747e4fSDavid du Colombier 	if(bind(nci->spec, "/net", MBEFORE) < 0){
7949a747e4fSDavid du Colombier 		logit("can't bind %s to /net: %r", nci->spec);
7959a747e4fSDavid du Colombier 		rv = reply("500 can't bind %s to /net: %r", nci->spec);
7967dd7cddfSDavid du Colombier 		unmount("#/", "/");
7977dd7cddfSDavid du Colombier 	}
7987dd7cddfSDavid du Colombier 
7999a747e4fSDavid du Colombier 	return rv;
8007dd7cddfSDavid du Colombier }
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier void
unmountnet(void)8037dd7cddfSDavid du Colombier unmountnet(void)
8047dd7cddfSDavid du Colombier {
8057dd7cddfSDavid du Colombier 	unmount(0, "/net");
8067dd7cddfSDavid du Colombier 	unmount("#/", "/");
8077dd7cddfSDavid du Colombier }
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier int
pasvcmd(char * arg)8107dd7cddfSDavid du Colombier pasvcmd(char *arg)
8117dd7cddfSDavid du Colombier {
8129a747e4fSDavid du Colombier 	NetConnInfo *nnci;
8137dd7cddfSDavid du Colombier 	Passive *p;
8147dd7cddfSDavid du Colombier 
8157dd7cddfSDavid du Colombier 	USED(arg);
8167dd7cddfSDavid du Colombier 	p = &passive;
8177dd7cddfSDavid du Colombier 
8187dd7cddfSDavid du Colombier 	if(p->inuse){
8197dd7cddfSDavid du Colombier 		close(p->afd);
8207dd7cddfSDavid du Colombier 		p->inuse = 0;
8217dd7cddfSDavid du Colombier 	}
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier 	if(mountnet() < 0)
8247dd7cddfSDavid du Colombier 		return 0;
8257dd7cddfSDavid du Colombier 
8267dd7cddfSDavid du Colombier 	p->afd = announce("tcp!*!0", passive.adir);
8277dd7cddfSDavid du Colombier 	if(p->afd < 0){
8287dd7cddfSDavid du Colombier 		unmountnet();
8297dd7cddfSDavid du Colombier 		return reply("500 No free ports");
8307dd7cddfSDavid du Colombier 	}
8319a747e4fSDavid du Colombier 	nnci = getnetconninfo(p->adir, -1);
83280ee5cbfSDavid du Colombier 	unmountnet();
8339a747e4fSDavid du Colombier 
8349a747e4fSDavid du Colombier 	/* parse the local address */
8359a747e4fSDavid du Colombier 	if(debug)
8369a747e4fSDavid du Colombier 		logit("local sys is %s", nci->lsys);
8379a747e4fSDavid du Colombier 	parseip(p->ipaddr, nci->lsys);
8389a747e4fSDavid du Colombier 	if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0)
8399a747e4fSDavid du Colombier 		parseip(p->ipaddr, nci->lsys);
8409a747e4fSDavid du Colombier 	p->port = atoi(nnci->lserv);
8419a747e4fSDavid du Colombier 
8429a747e4fSDavid du Colombier 	freenetconninfo(nnci);
8437dd7cddfSDavid du Colombier 	p->inuse = 1;
8447dd7cddfSDavid du Colombier 
8457dd7cddfSDavid du Colombier 	return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
8467dd7cddfSDavid du Colombier 		p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3],
8477dd7cddfSDavid du Colombier 		p->port>>8, p->port&0xff);
8487dd7cddfSDavid du Colombier }
8497dd7cddfSDavid du Colombier 
8507dd7cddfSDavid du Colombier enum
8517dd7cddfSDavid du Colombier {
8527dd7cddfSDavid du Colombier 	Narg=32,
8537dd7cddfSDavid du Colombier };
8547dd7cddfSDavid du Colombier int Cflag, rflag, tflag, Rflag;
8557dd7cddfSDavid du Colombier int maxnamelen;
8567dd7cddfSDavid du Colombier int col;
8577dd7cddfSDavid du Colombier 
8587dd7cddfSDavid du Colombier char*
mode2asc(int m)8597dd7cddfSDavid du Colombier mode2asc(int m)
8607dd7cddfSDavid du Colombier {
8617dd7cddfSDavid du Colombier 	static char asc[12];
8627dd7cddfSDavid du Colombier 	char *p;
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier 	strcpy(asc, "----------");
8659a747e4fSDavid du Colombier 	if(DMDIR & m)
8667dd7cddfSDavid du Colombier 		asc[0] = 'd';
8679a747e4fSDavid du Colombier 	if(DMAPPEND & m)
8687dd7cddfSDavid du Colombier 		asc[0] = 'a';
8699a747e4fSDavid du Colombier 	else if(DMEXCL & m)
8707dd7cddfSDavid du Colombier 		asc[3] = 'l';
8717dd7cddfSDavid du Colombier 
8727dd7cddfSDavid du Colombier 	for(p = asc+1; p < asc + 10; p += 3, m<<=3){
8737dd7cddfSDavid du Colombier 		if(m & 0400)
8747dd7cddfSDavid du Colombier 			p[0] = 'r';
8757dd7cddfSDavid du Colombier 		if(m & 0200)
8767dd7cddfSDavid du Colombier 			p[1] = 'w';
8777dd7cddfSDavid du Colombier 		if(m & 0100)
8787dd7cddfSDavid du Colombier 			p[2] = 'x';
8797dd7cddfSDavid du Colombier 	}
8807dd7cddfSDavid du Colombier 	return asc;
8817dd7cddfSDavid du Colombier }
8827dd7cddfSDavid du Colombier void
listfile(Biobufhdr * b,char * name,int lflag,char * dname)8837dd7cddfSDavid du Colombier listfile(Biobufhdr *b, char *name, int lflag, char *dname)
8847dd7cddfSDavid du Colombier {
8857dd7cddfSDavid du Colombier 	char ts[32];
886b27b55e2SDavid du Colombier 	int n, links, pad;
8877dd7cddfSDavid du Colombier 	long now;
8887dd7cddfSDavid du Colombier 	char *x;
8899a747e4fSDavid du Colombier 	Dir *d;
8907dd7cddfSDavid du Colombier 
8917dd7cddfSDavid du Colombier 	x = abspath(name);
8929a747e4fSDavid du Colombier 	if(x == nil)
8939a747e4fSDavid du Colombier 		return;
8949a747e4fSDavid du Colombier 	d = dirstat(x);
8959a747e4fSDavid du Colombier 	if(d == nil)
8967dd7cddfSDavid du Colombier 		return;
8977dd7cddfSDavid du Colombier 	if(isnone){
8987dd7cddfSDavid du Colombier 		if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0)
8999a747e4fSDavid du Colombier 			d->mode &= ~0222;
900b27b55e2SDavid du Colombier 		d->uid = "none";
901b27b55e2SDavid du Colombier 		d->gid = "none";
9027dd7cddfSDavid du Colombier 	}
9037dd7cddfSDavid du Colombier 
9049a747e4fSDavid du Colombier 	strcpy(ts, ctime(d->mtime));
9057dd7cddfSDavid du Colombier 	ts[16] = 0;
9067dd7cddfSDavid du Colombier 	now = time(0);
9079a747e4fSDavid du Colombier 	if(now - d->mtime > 6*30*24*60*60)
9087dd7cddfSDavid du Colombier 		memmove(ts+11, ts+23, 5);
9097dd7cddfSDavid du Colombier 	if(lflag){
9107dd7cddfSDavid du Colombier 		/* Unix style long listing */
9119a747e4fSDavid du Colombier 		if(DMDIR&d->mode){
9127dd7cddfSDavid du Colombier 			links = 2;
9139a747e4fSDavid du Colombier 			d->length = 512;
9147dd7cddfSDavid du Colombier 		} else
9157dd7cddfSDavid du Colombier 			links = 1;
9167dd7cddfSDavid du Colombier 
917b27b55e2SDavid du Colombier 		Bprint(b, "%s %3d %-8s %-8s %7lld %s ",
918b27b55e2SDavid du Colombier 			mode2asc(d->mode), links,
9199a747e4fSDavid du Colombier 			d->uid, d->gid, d->length, ts+4);
920b27b55e2SDavid du Colombier 	}
921b27b55e2SDavid du Colombier 	if(Cflag && maxnamelen < 40){
922b27b55e2SDavid du Colombier 		n = strlen(name);
923b27b55e2SDavid du Colombier 		pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1);
924b27b55e2SDavid du Colombier 		if(pad+maxnamelen+1 < 60){
925b27b55e2SDavid du Colombier 			Bprint(b, "%*s", pad-col+n, name);
926b27b55e2SDavid du Colombier 			col = pad+n;
927b27b55e2SDavid du Colombier 		}
928b27b55e2SDavid du Colombier 		else{
929b27b55e2SDavid du Colombier 			Bprint(b, "\r\n%s", name);
930b27b55e2SDavid du Colombier 			col = n;
931b27b55e2SDavid du Colombier 		}
932b27b55e2SDavid du Colombier 	}
933b27b55e2SDavid du Colombier 	else{
9347dd7cddfSDavid du Colombier 		if(dname)
935b27b55e2SDavid du Colombier 			Bprint(b, "%s/", dname);
936b27b55e2SDavid du Colombier 		Bprint(b, "%s\r\n", name);
9377dd7cddfSDavid du Colombier 	}
9389a747e4fSDavid du Colombier 	free(d);
9397dd7cddfSDavid du Colombier }
9407dd7cddfSDavid du Colombier int
dircomp(void * va,void * vb)9417dd7cddfSDavid du Colombier dircomp(void *va, void *vb)
9427dd7cddfSDavid du Colombier {
9437dd7cddfSDavid du Colombier 	int rv;
9447dd7cddfSDavid du Colombier 	Dir *a, *b;
9457dd7cddfSDavid du Colombier 
9467dd7cddfSDavid du Colombier 	a = va;
9477dd7cddfSDavid du Colombier 	b = vb;
9487dd7cddfSDavid du Colombier 
9497dd7cddfSDavid du Colombier 	if(tflag)
9507dd7cddfSDavid du Colombier 		rv = b->mtime - a->mtime;
9517dd7cddfSDavid du Colombier 	else
9527dd7cddfSDavid du Colombier 		rv = strcmp(a->name, b->name);
9537dd7cddfSDavid du Colombier 	return (rflag?-1:1)*rv;
9547dd7cddfSDavid du Colombier }
9557dd7cddfSDavid du Colombier void
listdir(char * name,Biobufhdr * b,int lflag,int * printname,Globlist * gl)9569a747e4fSDavid du Colombier listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl)
9577dd7cddfSDavid du Colombier {
9587dd7cddfSDavid du Colombier 	Dir *p;
959b27b55e2SDavid du Colombier 	int fd, n, i, l;
9607dd7cddfSDavid du Colombier 	char *dname;
9619a747e4fSDavid du Colombier 	uvlong total;
9627dd7cddfSDavid du Colombier 
9637dd7cddfSDavid du Colombier 	col = 0;
9647dd7cddfSDavid du Colombier 
9657dd7cddfSDavid du Colombier 	fd = open(name, OREAD);
9667dd7cddfSDavid du Colombier 	if(fd < 0){
9677dd7cddfSDavid du Colombier 		Bprint(b, "can't read %s: %r\r\n", name);
9687dd7cddfSDavid du Colombier 		return;
9697dd7cddfSDavid du Colombier 	}
9707dd7cddfSDavid du Colombier 	dname = 0;
9717dd7cddfSDavid du Colombier 	if(*printname){
9727dd7cddfSDavid du Colombier 		if(Rflag || lflag)
9737dd7cddfSDavid du Colombier 			Bprint(b, "\r\n%s:\r\n", name);
9747dd7cddfSDavid du Colombier 		else
9757dd7cddfSDavid du Colombier 			dname = name;
9767dd7cddfSDavid du Colombier 	}
9779a747e4fSDavid du Colombier 	n = dirreadall(fd, &p);
9789a747e4fSDavid du Colombier 	close(fd);
9797dd7cddfSDavid du Colombier 	if(Cflag){
980b27b55e2SDavid du Colombier 		for(i = 0; i < n; i++){
981b27b55e2SDavid du Colombier 			l = strlen(p[i].name);
982b27b55e2SDavid du Colombier 			if(l > maxnamelen)
983b27b55e2SDavid du Colombier 				maxnamelen = l;
984b27b55e2SDavid du Colombier 		}
9857dd7cddfSDavid du Colombier 	}
9867dd7cddfSDavid du Colombier 
9879a747e4fSDavid du Colombier 	/* Unix style total line */
9887dd7cddfSDavid du Colombier 	if(lflag){
9897dd7cddfSDavid du Colombier 		total = 0;
9907dd7cddfSDavid du Colombier 		for(i = 0; i < n; i++){
9919a747e4fSDavid du Colombier 			if(p[i].qid.type & QTDIR)
9927dd7cddfSDavid du Colombier 				total += 512;
9937dd7cddfSDavid du Colombier 			else
9947dd7cddfSDavid du Colombier 				total += p[i].length;
9957dd7cddfSDavid du Colombier 		}
9969a747e4fSDavid du Colombier 		Bprint(b, "total %ulld\r\n", total/512);
9977dd7cddfSDavid du Colombier 	}
9987dd7cddfSDavid du Colombier 
9997dd7cddfSDavid du Colombier 	qsort(p, n, sizeof(Dir), dircomp);
10007dd7cddfSDavid du Colombier 	for(i = 0; i < n; i++){
10019a747e4fSDavid du Colombier 		if(Rflag && (p[i].qid.type & QTDIR)){
10027dd7cddfSDavid du Colombier 			*printname = 1;
10039a747e4fSDavid du Colombier 			globadd(gl, name, p[i].name);
10047dd7cddfSDavid du Colombier 		}
10057dd7cddfSDavid du Colombier 		listfile(b, p[i].name, lflag, dname);
10067dd7cddfSDavid du Colombier 	}
10077dd7cddfSDavid du Colombier 	free(p);
10087dd7cddfSDavid du Colombier }
10097dd7cddfSDavid du Colombier void
list(char * arg,int lflag)10107dd7cddfSDavid du Colombier list(char *arg, int lflag)
10117dd7cddfSDavid du Colombier {
10129a747e4fSDavid du Colombier 	Dir *d;
10139a747e4fSDavid du Colombier 	Globlist *gl;
10149a747e4fSDavid du Colombier 	Glob *g;
10157dd7cddfSDavid du Colombier 	int dfd, printname;
10167dd7cddfSDavid du Colombier 	int i, n, argc;
10177dd7cddfSDavid du Colombier 	char *alist[Narg];
10187dd7cddfSDavid du Colombier 	char **argv;
10197dd7cddfSDavid du Colombier 	Biobufhdr bh;
10207dd7cddfSDavid du Colombier 	uchar buf[512];
1021b27b55e2SDavid du Colombier 	char *p, *s;
10227dd7cddfSDavid du Colombier 
10237dd7cddfSDavid du Colombier 	if(arg == 0)
10247dd7cddfSDavid du Colombier 		arg = "";
10257dd7cddfSDavid du Colombier 
10267dd7cddfSDavid du Colombier 	if(debug)
10277dd7cddfSDavid du Colombier 		logit("ls %s (. = %s)", arg, curdir);
10287dd7cddfSDavid du Colombier 
10297dd7cddfSDavid du Colombier 	/* process arguments, understand /bin/ls -l option */
10307dd7cddfSDavid du Colombier 	argv = alist;
10317dd7cddfSDavid du Colombier 	argv[0] = "/bin/ls";
10327dd7cddfSDavid du Colombier 	argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1;
10337dd7cddfSDavid du Colombier 	argv[argc] = 0;
10347dd7cddfSDavid du Colombier 	rflag = 0;
10357dd7cddfSDavid du Colombier 	tflag = 0;
10367dd7cddfSDavid du Colombier 	Rflag = 0;
10377dd7cddfSDavid du Colombier 	Cflag = 0;
10387dd7cddfSDavid du Colombier 	col = 0;
10397dd7cddfSDavid du Colombier 	ARGBEGIN{
10407dd7cddfSDavid du Colombier 	case 'l':
10417dd7cddfSDavid du Colombier 		lflag++;
10427dd7cddfSDavid du Colombier 		break;
10437dd7cddfSDavid du Colombier 	case 'R':
10447dd7cddfSDavid du Colombier 		Rflag++;
10457dd7cddfSDavid du Colombier 		break;
10467dd7cddfSDavid du Colombier 	case 'C':
10477dd7cddfSDavid du Colombier 		Cflag++;
10487dd7cddfSDavid du Colombier 		break;
10497dd7cddfSDavid du Colombier 	case 'r':
10507dd7cddfSDavid du Colombier 		rflag++;
10517dd7cddfSDavid du Colombier 		break;
10527dd7cddfSDavid du Colombier 	case 't':
10537dd7cddfSDavid du Colombier 		tflag++;
10547dd7cddfSDavid du Colombier 		break;
10557dd7cddfSDavid du Colombier 	}ARGEND;
10567dd7cddfSDavid du Colombier 	if(Cflag)
10577dd7cddfSDavid du Colombier 		lflag = 0;
10587dd7cddfSDavid du Colombier 
10597dd7cddfSDavid du Colombier 	dfd = dialdata();
10607dd7cddfSDavid du Colombier 	if(dfd < 0){
10617dd7cddfSDavid du Colombier 		reply("425 Error opening data connection:%r");
10627dd7cddfSDavid du Colombier 		return;
10637dd7cddfSDavid du Colombier 	}
10647dd7cddfSDavid du Colombier 	reply("150 Opened data connection (%s)", data);
10657dd7cddfSDavid du Colombier 
10667dd7cddfSDavid du Colombier 	Binits(&bh, dfd, OWRITE, buf, sizeof(buf));
10677dd7cddfSDavid du Colombier 	if(argc == 0){
10687dd7cddfSDavid du Colombier 		argc = 1;
10697dd7cddfSDavid du Colombier 		argv = alist;
10707dd7cddfSDavid du Colombier 		argv[0] = ".";
10717dd7cddfSDavid du Colombier 	}
1072b27b55e2SDavid du Colombier 
10737dd7cddfSDavid du Colombier 	for(i = 0; i < argc; i++){
10747dd7cddfSDavid du Colombier 		chdir(curdir);
1075b27b55e2SDavid du Colombier 		gl = glob(argv[i]);
10769a747e4fSDavid du Colombier 		if(gl == nil)
10779a747e4fSDavid du Colombier 			continue;
10789a747e4fSDavid du Colombier 
10799a747e4fSDavid du Colombier 		printname = gl->first != nil && gl->first->next != nil;
10807dd7cddfSDavid du Colombier 		maxnamelen = 8;
10817dd7cddfSDavid du Colombier 
10827dd7cddfSDavid du Colombier 		if(Cflag)
10839a747e4fSDavid du Colombier 			for(g = gl->first; g; g = g->next)
10849a747e4fSDavid du Colombier 				if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen)
10857dd7cddfSDavid du Colombier 					maxnamelen = n;
1086b27b55e2SDavid du Colombier 		while(s = globiter(gl)){
10879a747e4fSDavid du Colombier 			if(debug)
1088b27b55e2SDavid du Colombier 				logit("glob %s", s);
1089b27b55e2SDavid du Colombier 			p = abspath(s);
1090b27b55e2SDavid du Colombier 			if(p == nil){
1091b27b55e2SDavid du Colombier 				free(s);
10927dd7cddfSDavid du Colombier 				continue;
1093b27b55e2SDavid du Colombier 			}
10949a747e4fSDavid du Colombier 			d = dirstat(p);
1095b27b55e2SDavid du Colombier 			if(d == nil){
1096b27b55e2SDavid du Colombier 				free(s);
10977dd7cddfSDavid du Colombier 				continue;
1098b27b55e2SDavid du Colombier 			}
10999a747e4fSDavid du Colombier 			if(d->qid.type & QTDIR)
1100b27b55e2SDavid du Colombier 				listdir(s, &bh, lflag, &printname, gl);
11017dd7cddfSDavid du Colombier 			else
1102b27b55e2SDavid du Colombier 				listfile(&bh, s, lflag, 0);
1103b27b55e2SDavid du Colombier 			free(s);
11049a747e4fSDavid du Colombier 			free(d);
11057dd7cddfSDavid du Colombier 		}
11069a747e4fSDavid du Colombier 		globlistfree(gl);
11077dd7cddfSDavid du Colombier 	}
11087dd7cddfSDavid du Colombier 	if(Cflag)
11097dd7cddfSDavid du Colombier 		Bprint(&bh, "\r\n");
11107dd7cddfSDavid du Colombier 	Bflush(&bh);
11117dd7cddfSDavid du Colombier 	close(dfd);
11127dd7cddfSDavid du Colombier 
11137dd7cddfSDavid du Colombier 	reply("226 Transfer complete (list %s)", arg);
11147dd7cddfSDavid du Colombier }
11157dd7cddfSDavid du Colombier int
namelistcmd(char * arg)11167dd7cddfSDavid du Colombier namelistcmd(char *arg)
11177dd7cddfSDavid du Colombier {
11187dd7cddfSDavid du Colombier 	return asproc(list, arg, 0);
11197dd7cddfSDavid du Colombier }
11207dd7cddfSDavid du Colombier int
listcmd(char * arg)11217dd7cddfSDavid du Colombier listcmd(char *arg)
11227dd7cddfSDavid du Colombier {
11237dd7cddfSDavid du Colombier 	return asproc(list, arg, 1);
11247dd7cddfSDavid du Colombier }
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier /*
112707c70eb6SDavid du Colombier  * fuse compatability
112807c70eb6SDavid du Colombier  */
112907c70eb6SDavid du Colombier int
oksiteuser(void)113007c70eb6SDavid du Colombier oksiteuser(void)
113107c70eb6SDavid du Colombier {
113207c70eb6SDavid du Colombier 	char buf[64];
113307c70eb6SDavid du Colombier 	int fd, n;
113407c70eb6SDavid du Colombier 
113507c70eb6SDavid du Colombier 	fd = open("#c/user", OREAD);
113607c70eb6SDavid du Colombier 	if(fd < 0)
113707c70eb6SDavid du Colombier 		return 1;
113807c70eb6SDavid du Colombier 	n = read(fd, buf, sizeof buf - 1);
113907c70eb6SDavid du Colombier 	if(n > 0){
114007c70eb6SDavid du Colombier 		buf[n] = 0;
114107c70eb6SDavid du Colombier 		if(strcmp(buf, "none") == 0)
114207c70eb6SDavid du Colombier 			n = -1;
114307c70eb6SDavid du Colombier 	}
114407c70eb6SDavid du Colombier 	close(fd);
114507c70eb6SDavid du Colombier 	return n > 0;
114607c70eb6SDavid du Colombier }
114707c70eb6SDavid du Colombier 
114807c70eb6SDavid du Colombier int
sitecmd(char * arg)114907c70eb6SDavid du Colombier sitecmd(char *arg)
115007c70eb6SDavid du Colombier {
115107c70eb6SDavid du Colombier 	char *f[4];
115207c70eb6SDavid du Colombier 	int nf, r;
115307c70eb6SDavid du Colombier 	Dir *d;
115407c70eb6SDavid du Colombier 
1155*b3994199SDavid du Colombier 	if(arg == 0)
1156*b3994199SDavid du Colombier 		return reply("501 bad site command");
115707c70eb6SDavid du Colombier 	nf = tokenize(arg, f, nelem(f));
115807c70eb6SDavid du Colombier 	if(nf != 3 || cistrcmp(f[0], "chmod") != 0)
115907c70eb6SDavid du Colombier 		return reply("501 bad site command");
116007c70eb6SDavid du Colombier 	if(!oksiteuser())
116107c70eb6SDavid du Colombier 		return reply("550 Permission denied");
116207c70eb6SDavid du Colombier 	d = dirstat(f[2]);
116307c70eb6SDavid du Colombier 	if(d == nil)
116407c70eb6SDavid du Colombier 		return reply("501 site chmod: file does not exist");
116507c70eb6SDavid du Colombier 	d->mode &= ~0777;
116607c70eb6SDavid du Colombier 	d->mode |= strtoul(f[1], 0, 8) & 0777;
116707c70eb6SDavid du Colombier 	r = dirwstat(f[2], d);
116807c70eb6SDavid du Colombier 	free(d);
116907c70eb6SDavid du Colombier 	if(r < 0)
117007c70eb6SDavid du Colombier 		return reply("550 Permission denied %r");
117107c70eb6SDavid du Colombier 	return reply("200 very well, then");
117207c70eb6SDavid du Colombier  }
117307c70eb6SDavid du Colombier 
117407c70eb6SDavid du Colombier /*
11757dd7cddfSDavid du Colombier  *  return the size of the file
11767dd7cddfSDavid du Colombier  */
11777dd7cddfSDavid du Colombier int
sizecmd(char * arg)11787dd7cddfSDavid du Colombier sizecmd(char *arg)
11797dd7cddfSDavid du Colombier {
11809a747e4fSDavid du Colombier 	Dir *d;
11819a747e4fSDavid du Colombier 	int rv;
11827dd7cddfSDavid du Colombier 
11837dd7cddfSDavid du Colombier 	if(arg == 0)
11847dd7cddfSDavid du Colombier 		return reply("501 Size command requires pathname");
11857dd7cddfSDavid du Colombier 	arg = abspath(arg);
11869a747e4fSDavid du Colombier 	d = dirstat(arg);
11879a747e4fSDavid du Colombier 	if(d == nil)
11887dd7cddfSDavid du Colombier 		return reply("501 %r accessing %s", arg);
11899a747e4fSDavid du Colombier 	rv = reply("213 %lld", d->length);
11909a747e4fSDavid du Colombier 	free(d);
11919a747e4fSDavid du Colombier 	return rv;
11927dd7cddfSDavid du Colombier }
11937dd7cddfSDavid du Colombier 
11947dd7cddfSDavid du Colombier /*
11957dd7cddfSDavid du Colombier  *  return the modify time of the file
11967dd7cddfSDavid du Colombier  */
11977dd7cddfSDavid du Colombier int
mdtmcmd(char * arg)11987dd7cddfSDavid du Colombier mdtmcmd(char *arg)
11997dd7cddfSDavid du Colombier {
12009a747e4fSDavid du Colombier 	Dir *d;
12017dd7cddfSDavid du Colombier 	Tm *t;
12029a747e4fSDavid du Colombier 	int rv;
12037dd7cddfSDavid du Colombier 
12047dd7cddfSDavid du Colombier 	if(arg == 0)
12057dd7cddfSDavid du Colombier 		return reply("501 Mdtm command requires pathname");
12069a747e4fSDavid du Colombier 	if(arg == 0)
12079a747e4fSDavid du Colombier 		return reply("550 Permission denied");
12089a747e4fSDavid du Colombier 	d = dirstat(arg);
12099a747e4fSDavid du Colombier 	if(d == nil)
12107dd7cddfSDavid du Colombier 		return reply("501 %r accessing %s", arg);
12119a747e4fSDavid du Colombier 	t = gmtime(d->mtime);
12129a747e4fSDavid du Colombier 	rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
12137dd7cddfSDavid du Colombier 			t->year+1900, t->mon+1, t->mday,
12147dd7cddfSDavid du Colombier 			t->hour, t->min, t->sec);
12159a747e4fSDavid du Colombier 	free(d);
12169a747e4fSDavid du Colombier 	return rv;
12177dd7cddfSDavid du Colombier }
12187dd7cddfSDavid du Colombier 
12197dd7cddfSDavid du Colombier /*
12207dd7cddfSDavid du Colombier  *  set an offset to start reading a file from
12217dd7cddfSDavid du Colombier  *  only lasts for one command
12227dd7cddfSDavid du Colombier  */
12237dd7cddfSDavid du Colombier int
restartcmd(char * arg)12247dd7cddfSDavid du Colombier restartcmd(char *arg)
12257dd7cddfSDavid du Colombier {
12267dd7cddfSDavid du Colombier 	if(arg == 0)
12277dd7cddfSDavid du Colombier 		return reply("501 Restart command requires offset");
12287dd7cddfSDavid du Colombier 	offset = atoll(arg);
12297dd7cddfSDavid du Colombier 	if(offset < 0){
12307dd7cddfSDavid du Colombier 		offset = 0;
12317dd7cddfSDavid du Colombier 		return reply("501 Bad offset");
12327dd7cddfSDavid du Colombier 	}
12337dd7cddfSDavid du Colombier 
12347dd7cddfSDavid du Colombier 	return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset);
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier 
12377dd7cddfSDavid du Colombier /*
12387dd7cddfSDavid du Colombier  *  send a file to the user
12397dd7cddfSDavid du Colombier  */
12407dd7cddfSDavid du Colombier int
crlfwrite(int fd,char * p,int n)12417dd7cddfSDavid du Colombier crlfwrite(int fd, char *p, int n)
12427dd7cddfSDavid du Colombier {
12437dd7cddfSDavid du Colombier 	char *ep, *np;
12447dd7cddfSDavid du Colombier 	char buf[2*Nbuf];
12457dd7cddfSDavid du Colombier 
12467dd7cddfSDavid du Colombier 	for(np = buf, ep = p + n; p < ep; p++){
12477dd7cddfSDavid du Colombier 		if(*p == '\n')
12487dd7cddfSDavid du Colombier 			*np++ = '\r';
12497dd7cddfSDavid du Colombier 		*np++ = *p;
12507dd7cddfSDavid du Colombier 	}
12517dd7cddfSDavid du Colombier 	if(write(fd, buf, np - buf) == np - buf)
12527dd7cddfSDavid du Colombier 		return n;
12537dd7cddfSDavid du Colombier 	else
12547dd7cddfSDavid du Colombier 		return -1;
12557dd7cddfSDavid du Colombier }
12567dd7cddfSDavid du Colombier void
retrievedir(char * arg)12577dd7cddfSDavid du Colombier retrievedir(char *arg)
12587dd7cddfSDavid du Colombier {
12597dd7cddfSDavid du Colombier 	int n;
12607dd7cddfSDavid du Colombier 	char *p;
1261b27b55e2SDavid du Colombier 	String *file;
12627dd7cddfSDavid du Colombier 
12637dd7cddfSDavid du Colombier 	if(type != Timage){
12647dd7cddfSDavid du Colombier 		reply("550 This file requires type binary/image");
12657dd7cddfSDavid du Colombier 		return;
12667dd7cddfSDavid du Colombier 	}
12677dd7cddfSDavid du Colombier 
1268b27b55e2SDavid du Colombier 	file = s_copy(arg);
1269b27b55e2SDavid du Colombier 	p = strrchr(s_to_c(file), '/');
1270b27b55e2SDavid du Colombier 	if(p != s_to_c(file)){
12717dd7cddfSDavid du Colombier 		*p++ = 0;
1272b27b55e2SDavid du Colombier 		chdir(s_to_c(file));
12737dd7cddfSDavid du Colombier 	} else {
12747dd7cddfSDavid du Colombier 		chdir("/");
1275b27b55e2SDavid du Colombier 		p = s_to_c(file)+1;
12767dd7cddfSDavid du Colombier 	}
12777dd7cddfSDavid du Colombier 
12787dd7cddfSDavid du Colombier 	n = transfer("/bin/tar", "c", p, 0, 1);
12797dd7cddfSDavid du Colombier 	if(n < 0)
1280b27b55e2SDavid du Colombier 		logit("get %s failed", arg);
12817dd7cddfSDavid du Colombier 	else
1282b27b55e2SDavid du Colombier 		logit("get %s OK %d", arg, n);
1283b27b55e2SDavid du Colombier 	s_free(file);
12847dd7cddfSDavid du Colombier }
12857dd7cddfSDavid du Colombier void
retrieve(char * arg,int arg2)12867dd7cddfSDavid du Colombier retrieve(char *arg, int arg2)
12877dd7cddfSDavid du Colombier {
12887dd7cddfSDavid du Colombier 	int dfd, fd, n, i, bytes;
12899a747e4fSDavid du Colombier 	Dir *d;
12907dd7cddfSDavid du Colombier 	char buf[Nbuf];
12917dd7cddfSDavid du Colombier 	char *p, *ep;
12927dd7cddfSDavid du Colombier 
12937dd7cddfSDavid du Colombier 	USED(arg2);
12947dd7cddfSDavid du Colombier 
12957dd7cddfSDavid du Colombier 	p = strchr(arg, '\r');
12967dd7cddfSDavid du Colombier 	if(p){
12977dd7cddfSDavid du Colombier 		logit("cr in file name", arg);
12987dd7cddfSDavid du Colombier 		*p = 0;
12997dd7cddfSDavid du Colombier 	}
13007dd7cddfSDavid du Colombier 
13017dd7cddfSDavid du Colombier 	fd = open(arg, OREAD);
13027dd7cddfSDavid du Colombier 	if(fd == -1){
13037dd7cddfSDavid du Colombier 		n = strlen(arg);
13047dd7cddfSDavid du Colombier 		if(n > 4 && strcmp(arg+n-4, ".tar") == 0){
13057dd7cddfSDavid du Colombier 			*(arg+n-4) = 0;
13069a747e4fSDavid du Colombier 			d = dirstat(arg);
13079a747e4fSDavid du Colombier 			if(d != nil){
13089a747e4fSDavid du Colombier 				if(d->qid.type & QTDIR){
13097dd7cddfSDavid du Colombier 					retrievedir(arg);
13109a747e4fSDavid du Colombier 					free(d);
13117dd7cddfSDavid du Colombier 					return;
13127dd7cddfSDavid du Colombier 				}
13139a747e4fSDavid du Colombier 				free(d);
13149a747e4fSDavid du Colombier 			}
13157dd7cddfSDavid du Colombier 		}
13167dd7cddfSDavid du Colombier 		logit("get %s failed", arg);
13177dd7cddfSDavid du Colombier 		reply("550 Error opening %s: %r", arg);
13187dd7cddfSDavid du Colombier 		return;
13197dd7cddfSDavid du Colombier 	}
13207dd7cddfSDavid du Colombier 	if(offset != 0)
13219a747e4fSDavid du Colombier 		if(seek(fd, offset, 0) < 0){
13227dd7cddfSDavid du Colombier 			reply("550 %s: seek to %lld failed", arg, offset);
13237dd7cddfSDavid du Colombier 			close(fd);
13247dd7cddfSDavid du Colombier 			return;
13257dd7cddfSDavid du Colombier 		}
13269a747e4fSDavid du Colombier 	d = dirfstat(fd);
13279a747e4fSDavid du Colombier 	if(d != nil){
13289a747e4fSDavid du Colombier 		if(d->qid.type & QTDIR){
13297dd7cddfSDavid du Colombier 			reply("550 %s: not a plain file.", arg);
13307dd7cddfSDavid du Colombier 			close(fd);
13319a747e4fSDavid du Colombier 			free(d);
13327dd7cddfSDavid du Colombier 			return;
13337dd7cddfSDavid du Colombier 		}
13349a747e4fSDavid du Colombier 		free(d);
13357dd7cddfSDavid du Colombier 	}
13367dd7cddfSDavid du Colombier 
13377dd7cddfSDavid du Colombier 	n = read(fd, buf, sizeof(buf));
13387dd7cddfSDavid du Colombier 	if(n < 0){
13399a747e4fSDavid du Colombier 		logit("get %s failed", arg, mailaddr, nci->rsys);
13407dd7cddfSDavid du Colombier 		reply("550 Error reading %s: %r", arg);
13417dd7cddfSDavid du Colombier 		close(fd);
13427dd7cddfSDavid du Colombier 		return;
13437dd7cddfSDavid du Colombier 	}
13447dd7cddfSDavid du Colombier 
13457dd7cddfSDavid du Colombier 	if(type != Timage)
13467dd7cddfSDavid du Colombier 		for(p = buf, ep = &buf[n]; p < ep; p++)
13477dd7cddfSDavid du Colombier 			if(*p & 0x80){
13487dd7cddfSDavid du Colombier 				close(fd);
13497dd7cddfSDavid du Colombier 				reply("550 This file requires type binary/image");
13507dd7cddfSDavid du Colombier 				return;
13517dd7cddfSDavid du Colombier 			}
13527dd7cddfSDavid du Colombier 
13537dd7cddfSDavid du Colombier 	reply("150 Opening data connection for %s (%s)", arg, data);
13547dd7cddfSDavid du Colombier 	dfd = dialdata();
13557dd7cddfSDavid du Colombier 	if(dfd < 0){
13567dd7cddfSDavid du Colombier 		reply("425 Error opening data connection:%r");
13577dd7cddfSDavid du Colombier 		close(fd);
13587dd7cddfSDavid du Colombier 		return;
13597dd7cddfSDavid du Colombier 	}
13607dd7cddfSDavid du Colombier 
13617dd7cddfSDavid du Colombier 	bytes = 0;
13627dd7cddfSDavid du Colombier 	do {
13637dd7cddfSDavid du Colombier 		switch(type){
13647dd7cddfSDavid du Colombier 		case Timage:
13657dd7cddfSDavid du Colombier 			i = write(dfd, buf, n);
13667dd7cddfSDavid du Colombier 			break;
13677dd7cddfSDavid du Colombier 		default:
13687dd7cddfSDavid du Colombier 			i = crlfwrite(dfd, buf, n);
13697dd7cddfSDavid du Colombier 			break;
13707dd7cddfSDavid du Colombier 		}
13717dd7cddfSDavid du Colombier 		if(i != n){
13727dd7cddfSDavid du Colombier 			close(fd);
13737dd7cddfSDavid du Colombier 			close(dfd);
13747dd7cddfSDavid du Colombier 			logit("get %s %r to data connection after %d", arg, bytes);
13757dd7cddfSDavid du Colombier 			reply("550 Error writing to data connection: %r");
13767dd7cddfSDavid du Colombier 			return;
13777dd7cddfSDavid du Colombier 		}
13787dd7cddfSDavid du Colombier 		bytes += n;
13797dd7cddfSDavid du Colombier 	} while((n = read(fd, buf, sizeof(buf))) > 0);
13807dd7cddfSDavid du Colombier 
13817dd7cddfSDavid du Colombier 	if(n < 0)
13827dd7cddfSDavid du Colombier 		logit("get %s %r after %d", arg, bytes);
13837dd7cddfSDavid du Colombier 
13847dd7cddfSDavid du Colombier 	close(fd);
13857dd7cddfSDavid du Colombier 	close(dfd);
13867dd7cddfSDavid du Colombier 	reply("226 Transfer complete");
13877dd7cddfSDavid du Colombier 	logit("get %s OK %d", arg, bytes);
13887dd7cddfSDavid du Colombier }
13897dd7cddfSDavid du Colombier int
retrievecmd(char * arg)13907dd7cddfSDavid du Colombier retrievecmd(char *arg)
13917dd7cddfSDavid du Colombier {
13927dd7cddfSDavid du Colombier 	if(arg == 0)
13937dd7cddfSDavid du Colombier 		return reply("501 Retrieve command requires an argument");
13947dd7cddfSDavid du Colombier 	arg = abspath(arg);
13959a747e4fSDavid du Colombier 	if(arg == 0)
13969a747e4fSDavid du Colombier 		return reply("550 Permission denied");
13977dd7cddfSDavid du Colombier 
13987dd7cddfSDavid du Colombier 	return asproc(retrieve, arg, 0);
13997dd7cddfSDavid du Colombier }
14007dd7cddfSDavid du Colombier 
14017dd7cddfSDavid du Colombier /*
14027dd7cddfSDavid du Colombier  *  get a file from the user
14037dd7cddfSDavid du Colombier  */
14047dd7cddfSDavid du Colombier int
lfwrite(int fd,char * p,int n)14057dd7cddfSDavid du Colombier lfwrite(int fd, char *p, int n)
14067dd7cddfSDavid du Colombier {
14077dd7cddfSDavid du Colombier 	char *ep, *np;
14087dd7cddfSDavid du Colombier 	char buf[Nbuf];
14097dd7cddfSDavid du Colombier 
14107dd7cddfSDavid du Colombier 	for(np = buf, ep = p + n; p < ep; p++){
14117dd7cddfSDavid du Colombier 		if(*p != '\r')
14127dd7cddfSDavid du Colombier 			*np++ = *p;
14137dd7cddfSDavid du Colombier 	}
14147dd7cddfSDavid du Colombier 	if(write(fd, buf, np - buf) == np - buf)
14157dd7cddfSDavid du Colombier 		return n;
14167dd7cddfSDavid du Colombier 	else
14177dd7cddfSDavid du Colombier 		return -1;
14187dd7cddfSDavid du Colombier }
14197dd7cddfSDavid du Colombier void
store(char * arg,int fd)14207dd7cddfSDavid du Colombier store(char *arg, int fd)
14217dd7cddfSDavid du Colombier {
14227dd7cddfSDavid du Colombier 	int dfd, n, i;
14237dd7cddfSDavid du Colombier 	char buf[Nbuf];
14247dd7cddfSDavid du Colombier 
14257dd7cddfSDavid du Colombier 	reply("150 Opening data connection for %s (%s)", arg, data);
14267dd7cddfSDavid du Colombier 	dfd = dialdata();
14277dd7cddfSDavid du Colombier 	if(dfd < 0){
14287dd7cddfSDavid du Colombier 		reply("425 Error opening data connection:%r");
14297dd7cddfSDavid du Colombier 		close(fd);
14307dd7cddfSDavid du Colombier 		return;
14317dd7cddfSDavid du Colombier 	}
14327dd7cddfSDavid du Colombier 
14337dd7cddfSDavid du Colombier 	while((n = read(dfd, buf, sizeof(buf))) > 0){
14347dd7cddfSDavid du Colombier 		switch(type){
14357dd7cddfSDavid du Colombier 		case Timage:
14367dd7cddfSDavid du Colombier 			i = write(fd, buf, n);
14377dd7cddfSDavid du Colombier 			break;
14387dd7cddfSDavid du Colombier 		default:
14397dd7cddfSDavid du Colombier 			i = lfwrite(fd, buf, n);
14407dd7cddfSDavid du Colombier 			break;
14417dd7cddfSDavid du Colombier 		}
14427dd7cddfSDavid du Colombier 		if(i != n){
14437dd7cddfSDavid du Colombier 			close(fd);
14447dd7cddfSDavid du Colombier 			close(dfd);
14457dd7cddfSDavid du Colombier 			reply("550 Error writing file");
14467dd7cddfSDavid du Colombier 			return;
14477dd7cddfSDavid du Colombier 		}
14487dd7cddfSDavid du Colombier 	}
14497dd7cddfSDavid du Colombier 	close(fd);
14507dd7cddfSDavid du Colombier 	close(dfd);
14517dd7cddfSDavid du Colombier 	logit("put %s OK", arg);
14527dd7cddfSDavid du Colombier 	reply("226 Transfer complete");
14537dd7cddfSDavid du Colombier }
14547dd7cddfSDavid du Colombier int
storecmd(char * arg)14557dd7cddfSDavid du Colombier storecmd(char *arg)
14567dd7cddfSDavid du Colombier {
14577dd7cddfSDavid du Colombier 	int fd, rv;
14587dd7cddfSDavid du Colombier 
14597dd7cddfSDavid du Colombier 	if(arg == 0)
14607dd7cddfSDavid du Colombier 		return reply("501 Store command requires an argument");
14617dd7cddfSDavid du Colombier 	arg = abspath(arg);
14629a747e4fSDavid du Colombier 	if(arg == 0)
14639a747e4fSDavid du Colombier 		return reply("550 Permission denied");
14647dd7cddfSDavid du Colombier 	if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1))
14657dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
14667dd7cddfSDavid du Colombier 	if(offset){
14677dd7cddfSDavid du Colombier 		fd = open(arg, OWRITE);
14687dd7cddfSDavid du Colombier 		if(fd == -1)
14697dd7cddfSDavid du Colombier 			return reply("550 Error opening %s: %r", arg);
14709a747e4fSDavid du Colombier 		if(seek(fd, offset, 0) == -1)
14717dd7cddfSDavid du Colombier 			return reply("550 Error seeking %s to %d: %r",
14727dd7cddfSDavid du Colombier 				arg, offset);
14737dd7cddfSDavid du Colombier 	} else {
14747dd7cddfSDavid du Colombier 		fd = create(arg, OWRITE, createperm);
14757dd7cddfSDavid du Colombier 		if(fd == -1)
14767dd7cddfSDavid du Colombier 			return reply("550 Error creating %s: %r", arg);
14777dd7cddfSDavid du Colombier 	}
14787dd7cddfSDavid du Colombier 
14797dd7cddfSDavid du Colombier 	rv = asproc(store, arg, fd);
14807dd7cddfSDavid du Colombier 	close(fd);
14817dd7cddfSDavid du Colombier 	return rv;
14827dd7cddfSDavid du Colombier }
14837dd7cddfSDavid du Colombier int
appendcmd(char * arg)14847dd7cddfSDavid du Colombier appendcmd(char *arg)
14857dd7cddfSDavid du Colombier {
14867dd7cddfSDavid du Colombier 	int fd, rv;
14877dd7cddfSDavid du Colombier 
14887dd7cddfSDavid du Colombier 	if(arg == 0)
14897dd7cddfSDavid du Colombier 		return reply("501 Append command requires an argument");
14907dd7cddfSDavid du Colombier 	if(isnone)
14917dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
14927dd7cddfSDavid du Colombier 	arg = abspath(arg);
14939a747e4fSDavid du Colombier 	if(arg == 0)
14949a747e4fSDavid du Colombier 		return reply("550 Error creating %s: Permission denied", arg);
14957dd7cddfSDavid du Colombier 	fd = open(arg, OWRITE);
14967dd7cddfSDavid du Colombier 	if(fd == -1){
14977dd7cddfSDavid du Colombier 		fd = create(arg, OWRITE, createperm);
14987dd7cddfSDavid du Colombier 		if(fd == -1)
14997dd7cddfSDavid du Colombier 			return reply("550 Error creating %s: %r", arg);
15007dd7cddfSDavid du Colombier 	}
15017dd7cddfSDavid du Colombier 	seek(fd, 0, 2);
15027dd7cddfSDavid du Colombier 
15037dd7cddfSDavid du Colombier 	rv = asproc(store, arg, fd);
15047dd7cddfSDavid du Colombier 	close(fd);
15057dd7cddfSDavid du Colombier 	return rv;
15067dd7cddfSDavid du Colombier }
15077dd7cddfSDavid du Colombier int
storeucmd(char * arg)15087dd7cddfSDavid du Colombier storeucmd(char *arg)
15097dd7cddfSDavid du Colombier {
15107dd7cddfSDavid du Colombier 	int fd, rv;
15119a747e4fSDavid du Colombier 	char name[Maxpath];
15127dd7cddfSDavid du Colombier 
15137dd7cddfSDavid du Colombier 	USED(arg);
15147dd7cddfSDavid du Colombier 	if(isnone)
15157dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
1516b27b55e2SDavid du Colombier 	strncpy(name, "ftpXXXXXXXXXXX", sizeof name);
15177dd7cddfSDavid du Colombier 	mktemp(name);
15187dd7cddfSDavid du Colombier 	fd = create(name, OWRITE, createperm);
15197dd7cddfSDavid du Colombier 	if(fd == -1)
15207dd7cddfSDavid du Colombier 		return reply("550 Error creating %s: %r", name);
15217dd7cddfSDavid du Colombier 
15227dd7cddfSDavid du Colombier 	rv = asproc(store, name, fd);
15237dd7cddfSDavid du Colombier 	close(fd);
15247dd7cddfSDavid du Colombier 	return rv;
15257dd7cddfSDavid du Colombier }
15267dd7cddfSDavid du Colombier 
15277dd7cddfSDavid du Colombier int
mkdircmd(char * name)15287dd7cddfSDavid du Colombier mkdircmd(char *name)
15297dd7cddfSDavid du Colombier {
15307dd7cddfSDavid du Colombier 	int fd;
15317dd7cddfSDavid du Colombier 
15327dd7cddfSDavid du Colombier 	if(name == 0)
15337dd7cddfSDavid du Colombier 		return reply("501 Mkdir command requires an argument");
15347dd7cddfSDavid du Colombier 	if(isnone)
15357dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
15367dd7cddfSDavid du Colombier 	name = abspath(name);
15379a747e4fSDavid du Colombier 	if(name == 0)
15389a747e4fSDavid du Colombier 		return reply("550 Permission denied");
15399a747e4fSDavid du Colombier 	fd = create(name, OREAD, DMDIR|0775);
15407dd7cddfSDavid du Colombier 	if(fd < 0)
15417dd7cddfSDavid du Colombier 		return reply("550 Can't create %s: %r", name);
15427dd7cddfSDavid du Colombier 	close(fd);
15437dd7cddfSDavid du Colombier 	return reply("226 %s created", name);
15447dd7cddfSDavid du Colombier }
15457dd7cddfSDavid du Colombier 
15467dd7cddfSDavid du Colombier int
delcmd(char * name)15477dd7cddfSDavid du Colombier delcmd(char *name)
15487dd7cddfSDavid du Colombier {
15497dd7cddfSDavid du Colombier 	if(name == 0)
15507dd7cddfSDavid du Colombier 		return reply("501 Rmdir/delete command requires an argument");
15517dd7cddfSDavid du Colombier 	if(isnone)
15527dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
15537dd7cddfSDavid du Colombier 	name = abspath(name);
15549a747e4fSDavid du Colombier 	if(name == 0)
15559a747e4fSDavid du Colombier 		return reply("550 Permission denied");
15567dd7cddfSDavid du Colombier 	if(remove(name) < 0)
15577dd7cddfSDavid du Colombier 		return reply("550 Can't remove %s: %r", name);
15587dd7cddfSDavid du Colombier 	else
15597dd7cddfSDavid du Colombier 		return reply("226 %s removed", name);
15607dd7cddfSDavid du Colombier }
15617dd7cddfSDavid du Colombier 
15627dd7cddfSDavid du Colombier /*
15637dd7cddfSDavid du Colombier  *  kill off the last transfer (if the process still exists)
15647dd7cddfSDavid du Colombier  */
15657dd7cddfSDavid du Colombier int
abortcmd(char * arg)15667dd7cddfSDavid du Colombier abortcmd(char *arg)
15677dd7cddfSDavid du Colombier {
15687dd7cddfSDavid du Colombier 	USED(arg);
1569b27b55e2SDavid du Colombier 
1570b27b55e2SDavid du Colombier 	logit("abort pid %d", pid);
1571b27b55e2SDavid du Colombier 	if(pid){
1572b27b55e2SDavid du Colombier 		if(postnote(PNPROC, pid, "kill") == 0)
15737dd7cddfSDavid du Colombier 			reply("426 Command aborted");
1574b27b55e2SDavid du Colombier 		else
1575b27b55e2SDavid du Colombier 			logit("postnote pid %d %r", pid);
1576b27b55e2SDavid du Colombier 	}
15777dd7cddfSDavid du Colombier 	return reply("226 Abort processed");
15787dd7cddfSDavid du Colombier }
15797dd7cddfSDavid du Colombier 
15807dd7cddfSDavid du Colombier int
systemcmd(char * arg)15817dd7cddfSDavid du Colombier systemcmd(char *arg)
15827dd7cddfSDavid du Colombier {
15837dd7cddfSDavid du Colombier 	USED(arg);
15847dd7cddfSDavid du Colombier 	return reply("215 UNIX Type: L8 Version: Plan 9");
15857dd7cddfSDavid du Colombier }
15867dd7cddfSDavid du Colombier 
15877dd7cddfSDavid du Colombier int
helpcmd(char * arg)15887dd7cddfSDavid du Colombier helpcmd(char *arg)
15897dd7cddfSDavid du Colombier {
15907dd7cddfSDavid du Colombier 	int i;
15919a747e4fSDavid du Colombier 	char buf[80];
15929a747e4fSDavid du Colombier 	char *p, *e;
15937dd7cddfSDavid du Colombier 
15947dd7cddfSDavid du Colombier 	USED(arg);
15957dd7cddfSDavid du Colombier 	reply("214- the following commands are implemented:");
15969a747e4fSDavid du Colombier 	p = buf;
15979a747e4fSDavid du Colombier 	e = buf+sizeof buf;
15987dd7cddfSDavid du Colombier 	for(i = 0; cmdtab[i].name; i++){
15999a747e4fSDavid du Colombier 		if((i%8) == 0){
16009a747e4fSDavid du Colombier 			reply("214-%s", buf);
16019a747e4fSDavid du Colombier 			p = buf;
16027dd7cddfSDavid du Colombier 		}
16039a747e4fSDavid du Colombier 		p = seprint(p, e, " %-5.5s", cmdtab[i].name);
16049a747e4fSDavid du Colombier 	}
16059a747e4fSDavid du Colombier 	if(p != buf)
16069a747e4fSDavid du Colombier 		reply("214-%s", buf);
16079a747e4fSDavid du Colombier 	reply("214 ");
16087dd7cddfSDavid du Colombier 	return 0;
16097dd7cddfSDavid du Colombier }
16107dd7cddfSDavid du Colombier 
16117dd7cddfSDavid du Colombier /*
16127dd7cddfSDavid du Colombier  *  renaming a file takes two commands
16137dd7cddfSDavid du Colombier  */
1614b27b55e2SDavid du Colombier static String *filepath;
16157dd7cddfSDavid du Colombier 
16167dd7cddfSDavid du Colombier int
rnfrcmd(char * from)16177dd7cddfSDavid du Colombier rnfrcmd(char *from)
16187dd7cddfSDavid du Colombier {
16197dd7cddfSDavid du Colombier 	if(isnone)
16207dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
16217dd7cddfSDavid du Colombier 	if(from == 0)
16227dd7cddfSDavid du Colombier 		return reply("501 Rename command requires an argument");
16237dd7cddfSDavid du Colombier 	from = abspath(from);
16249a747e4fSDavid du Colombier 	if(from == 0)
16259a747e4fSDavid du Colombier 		return reply("550 Permission denied");
1626b27b55e2SDavid du Colombier 	if(filepath == nil)
1627b27b55e2SDavid du Colombier 		filepath = s_copy(from);
1628b27b55e2SDavid du Colombier 	else{
1629b27b55e2SDavid du Colombier 		s_reset(filepath);
1630b27b55e2SDavid du Colombier 		s_append(filepath, from);
1631b27b55e2SDavid du Colombier 	}
1632b27b55e2SDavid du Colombier 	return reply("350 Rename %s to ...", s_to_c(filepath));
16337dd7cddfSDavid du Colombier }
16347dd7cddfSDavid du Colombier int
rntocmd(char * to)16357dd7cddfSDavid du Colombier rntocmd(char *to)
16367dd7cddfSDavid du Colombier {
1637b27b55e2SDavid du Colombier 	int r;
1638d3c05884SDavid du Colombier 	Dir nd;
1639b27b55e2SDavid du Colombier 	char *fp, *tp;
16407dd7cddfSDavid du Colombier 
16417dd7cddfSDavid du Colombier 	if(isnone)
16427dd7cddfSDavid du Colombier 		return reply("550 Permission denied");
16437dd7cddfSDavid du Colombier 	if(to == 0)
16447dd7cddfSDavid du Colombier 		return reply("501 Rename command requires an argument");
16457dd7cddfSDavid du Colombier 	to = abspath(to);
16469a747e4fSDavid du Colombier 	if(to == 0)
16479a747e4fSDavid du Colombier 		return reply("550 Permission denied");
1648b27b55e2SDavid du Colombier 	if(filepath == nil || *(s_to_c(filepath)) == 0)
16497dd7cddfSDavid du Colombier 		return reply("503 Rnto must be preceeded by an rnfr");
16507dd7cddfSDavid du Colombier 
16517dd7cddfSDavid du Colombier 	tp = strrchr(to, '/');
1652b27b55e2SDavid du Colombier 	fp = strrchr(s_to_c(filepath), '/');
16537dd7cddfSDavid du Colombier 	if((tp && fp == 0) || (fp && tp == 0)
1654b27b55e2SDavid du Colombier 	|| (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to))))
16557dd7cddfSDavid du Colombier 		return reply("550 Rename can't change directory");
16567dd7cddfSDavid du Colombier 	if(tp)
16577dd7cddfSDavid du Colombier 		to = tp+1;
16587dd7cddfSDavid du Colombier 
1659d3c05884SDavid du Colombier 	nulldir(&nd);
1660d3c05884SDavid du Colombier 	nd.name = to;
1661b27b55e2SDavid du Colombier 	if(dirwstat(s_to_c(filepath), &nd) < 0)
1662b27b55e2SDavid du Colombier 		r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to);
1663b27b55e2SDavid du Colombier 	else
1664b27b55e2SDavid du Colombier 		r = reply("250 %s now %s", s_to_c(filepath), to);
1665b27b55e2SDavid du Colombier 	s_reset(filepath);
16667dd7cddfSDavid du Colombier 
1667b27b55e2SDavid du Colombier 	return r;
16687dd7cddfSDavid du Colombier }
16697dd7cddfSDavid du Colombier 
16707dd7cddfSDavid du Colombier /*
16717dd7cddfSDavid du Colombier  *  to dial out we need the network file system in our
16727dd7cddfSDavid du Colombier  *  name space.
16737dd7cddfSDavid du Colombier  */
16747dd7cddfSDavid du Colombier int
dialdata(void)16757dd7cddfSDavid du Colombier dialdata(void)
16767dd7cddfSDavid du Colombier {
16777dd7cddfSDavid du Colombier 	int fd, cfd;
16787dd7cddfSDavid du Colombier 	char ldir[40];
16799a747e4fSDavid du Colombier 	char err[Maxerr];
16807dd7cddfSDavid du Colombier 
16817dd7cddfSDavid du Colombier 	if(mountnet() < 0)
16827dd7cddfSDavid du Colombier 		return -1;
16837dd7cddfSDavid du Colombier 
16847dd7cddfSDavid du Colombier 	if(!passive.inuse){
16857dd7cddfSDavid du Colombier 		fd = dial(data, "20", 0, 0);
16869a747e4fSDavid du Colombier 		errstr(err, sizeof err);
16877dd7cddfSDavid du Colombier 	} else {
16887dd7cddfSDavid du Colombier 		alarm(5*60*1000);
16897dd7cddfSDavid du Colombier 		cfd = listen(passive.adir, ldir);
16907dd7cddfSDavid du Colombier 		alarm(0);
16919a747e4fSDavid du Colombier 		errstr(err, sizeof err);
16927dd7cddfSDavid du Colombier 		if(cfd < 0)
16937dd7cddfSDavid du Colombier 			return -1;
16947dd7cddfSDavid du Colombier 		fd = accept(cfd, ldir);
16959a747e4fSDavid du Colombier 		errstr(err, sizeof err);
16967dd7cddfSDavid du Colombier 		close(cfd);
16977dd7cddfSDavid du Colombier 	}
16987dd7cddfSDavid du Colombier 	if(fd < 0)
16997dd7cddfSDavid du Colombier 		logit("can't dial %s: %s", data, err);
17007dd7cddfSDavid du Colombier 
17017dd7cddfSDavid du Colombier 	unmountnet();
17029a747e4fSDavid du Colombier 	werrstr(err, sizeof err);
17037dd7cddfSDavid du Colombier 	return fd;
17047dd7cddfSDavid du Colombier }
17057dd7cddfSDavid du Colombier 
17067dd7cddfSDavid du Colombier int
postnote(int group,int pid,char * note)17077dd7cddfSDavid du Colombier postnote(int group, int pid, char *note)
17087dd7cddfSDavid du Colombier {
17097dd7cddfSDavid du Colombier 	char file[128];
17107dd7cddfSDavid du Colombier 	int f, r;
17117dd7cddfSDavid du Colombier 
1712b27b55e2SDavid du Colombier 	/*
1713b27b55e2SDavid du Colombier 	 * Use #p because /proc may not be in the namespace.
1714b27b55e2SDavid du Colombier 	 */
17157dd7cddfSDavid du Colombier 	switch(group) {
17167dd7cddfSDavid du Colombier 	case PNPROC:
17177dd7cddfSDavid du Colombier 		sprint(file, "#p/%d/note", pid);
17187dd7cddfSDavid du Colombier 		break;
17197dd7cddfSDavid du Colombier 	case PNGROUP:
17207dd7cddfSDavid du Colombier 		sprint(file, "#p/%d/notepg", pid);
17217dd7cddfSDavid du Colombier 		break;
17227dd7cddfSDavid du Colombier 	default:
17237dd7cddfSDavid du Colombier 		return -1;
17247dd7cddfSDavid du Colombier 	}
17257dd7cddfSDavid du Colombier 
17267dd7cddfSDavid du Colombier 	f = open(file, OWRITE);
17277dd7cddfSDavid du Colombier 	if(f < 0)
17287dd7cddfSDavid du Colombier 		return -1;
17297dd7cddfSDavid du Colombier 
17307dd7cddfSDavid du Colombier 	r = strlen(note);
17317dd7cddfSDavid du Colombier 	if(write(f, note, r) != r) {
17327dd7cddfSDavid du Colombier 		close(f);
17337dd7cddfSDavid du Colombier 		return -1;
17347dd7cddfSDavid du Colombier 	}
17357dd7cddfSDavid du Colombier 	close(f);
17367dd7cddfSDavid du Colombier 	return 0;
17377dd7cddfSDavid du Colombier }
17389a747e4fSDavid du Colombier 
1739b27b55e2SDavid du Colombier /*
1740b27b55e2SDavid du Colombier  *  to circumscribe the accessible files we have to eliminate ..'s
1741b27b55e2SDavid du Colombier  *  and resolve all names from the root.  We also remove any /bin/rc
1742b27b55e2SDavid du Colombier  *  special characters to avoid later problems with executed commands.
1743b27b55e2SDavid du Colombier  */
1744b27b55e2SDavid du Colombier char *special = "`;| ";
1745b27b55e2SDavid du Colombier 
1746b27b55e2SDavid du Colombier char*
abspath(char * origpath)1747b27b55e2SDavid du Colombier abspath(char *origpath)
1748b27b55e2SDavid du Colombier {
1749b27b55e2SDavid du Colombier 	char *p, *sp, *path;
1750b27b55e2SDavid du Colombier 	static String *rpath;
1751b27b55e2SDavid du Colombier 
1752b27b55e2SDavid du Colombier 	if(rpath == nil)
1753b27b55e2SDavid du Colombier 		rpath = s_new();
1754b27b55e2SDavid du Colombier 	else
1755b27b55e2SDavid du Colombier 		s_reset(rpath);
1756b27b55e2SDavid du Colombier 
1757b27b55e2SDavid du Colombier 	if(origpath == nil)
1758b27b55e2SDavid du Colombier 		s_append(rpath, curdir);
1759b27b55e2SDavid du Colombier 	else{
1760b27b55e2SDavid du Colombier 		if(*origpath != '/'){
1761b27b55e2SDavid du Colombier 			s_append(rpath, curdir);
1762b27b55e2SDavid du Colombier 			s_append(rpath, "/");
1763b27b55e2SDavid du Colombier 		}
1764b27b55e2SDavid du Colombier 		s_append(rpath, origpath);
1765b27b55e2SDavid du Colombier 	}
1766b27b55e2SDavid du Colombier 	path = s_to_c(rpath);
1767b27b55e2SDavid du Colombier 
1768b27b55e2SDavid du Colombier 	for(sp = special; *sp; sp++){
1769b27b55e2SDavid du Colombier 		p = strchr(path, *sp);
1770b27b55e2SDavid du Colombier 		if(p)
1771b27b55e2SDavid du Colombier 			*p = 0;
1772b27b55e2SDavid du Colombier 	}
1773b27b55e2SDavid du Colombier 
1774b27b55e2SDavid du Colombier 	cleanname(s_to_c(rpath));
1775b27b55e2SDavid du Colombier 	rpath->ptr = rpath->base+strlen(rpath->base);
1776b27b55e2SDavid du Colombier 
1777b27b55e2SDavid du Colombier 	if(!accessok(s_to_c(rpath)))
1778b27b55e2SDavid du Colombier 		return nil;
1779b27b55e2SDavid du Colombier 
1780b27b55e2SDavid du Colombier 	return s_to_c(rpath);
1781b27b55e2SDavid du Colombier }
17829a747e4fSDavid du Colombier 
17839a747e4fSDavid du Colombier typedef struct Path Path;
17849a747e4fSDavid du Colombier struct Path {
17859a747e4fSDavid du Colombier 	Path	*next;
1786b27b55e2SDavid du Colombier 	String	*path;
17879a747e4fSDavid du Colombier 	int	inuse;
17889a747e4fSDavid du Colombier 	int	ok;
17899a747e4fSDavid du Colombier };
17909a747e4fSDavid du Colombier 
17919a747e4fSDavid du Colombier enum
17929a747e4fSDavid du Colombier {
17939a747e4fSDavid du Colombier 	Maxlevel = 16,
17949a747e4fSDavid du Colombier 	Maxperlevel= 8,
17959a747e4fSDavid du Colombier };
17969a747e4fSDavid du Colombier 
17979a747e4fSDavid du Colombier Path *pathlevel[Maxlevel];
17989a747e4fSDavid du Colombier 
17999a747e4fSDavid du Colombier Path*
unlinkpath(char * path,int level)18009a747e4fSDavid du Colombier unlinkpath(char *path, int level)
18019a747e4fSDavid du Colombier {
1802b27b55e2SDavid du Colombier 	String *s;
18039a747e4fSDavid du Colombier 	Path **l, *p;
18049a747e4fSDavid du Colombier 	int n;
18059a747e4fSDavid du Colombier 
18069a747e4fSDavid du Colombier 	n = 0;
18079a747e4fSDavid du Colombier 	for(l = &pathlevel[level]; *l; l = &(*l)->next){
18089a747e4fSDavid du Colombier 		p = *l;
1809b27b55e2SDavid du Colombier 		/* hit */
1810b27b55e2SDavid du Colombier 		if(strcmp(s_to_c(p->path), path) == 0){
18119a747e4fSDavid du Colombier 			*l = p->next;
18129a747e4fSDavid du Colombier 			p->next = nil;
18139a747e4fSDavid du Colombier 			return p;
18149a747e4fSDavid du Colombier 		}
18159a747e4fSDavid du Colombier 		/* reuse */
1816b27b55e2SDavid du Colombier 		if(++n >= Maxperlevel){
18179a747e4fSDavid du Colombier 			*l = p->next;
1818b27b55e2SDavid du Colombier 			s = p->path;
1819b27b55e2SDavid du Colombier 			s_reset(p->path);
18209a747e4fSDavid du Colombier 			memset(p, 0, sizeof *p);
1821b27b55e2SDavid du Colombier 			p->path = s_append(s, path);
1822b27b55e2SDavid du Colombier 			return p;
1823b27b55e2SDavid du Colombier 		}
1824b27b55e2SDavid du Colombier 	}
18259a747e4fSDavid du Colombier 
18269a747e4fSDavid du Colombier 	/* allocate */
18279a747e4fSDavid du Colombier 	p = mallocz(sizeof *p, 1);
1828b27b55e2SDavid du Colombier 	p->path = s_copy(path);
18299a747e4fSDavid du Colombier 	return p;
18309a747e4fSDavid du Colombier }
18319a747e4fSDavid du Colombier 
18329a747e4fSDavid du Colombier void
linkpath(Path * p,int level)18339a747e4fSDavid du Colombier linkpath(Path *p, int level)
18349a747e4fSDavid du Colombier {
18359a747e4fSDavid du Colombier 	p->next = pathlevel[level];
18369a747e4fSDavid du Colombier 	pathlevel[level] = p;
18379a747e4fSDavid du Colombier 	p->inuse = 1;
18389a747e4fSDavid du Colombier }
18399a747e4fSDavid du Colombier 
18409a747e4fSDavid du Colombier void
addpath(Path * p,int level,int ok)18419a747e4fSDavid du Colombier addpath(Path *p, int level, int ok)
18429a747e4fSDavid du Colombier {
18439a747e4fSDavid du Colombier 	p->ok = ok;
18449a747e4fSDavid du Colombier 	p->next = pathlevel[level];
18459a747e4fSDavid du Colombier 	pathlevel[level] = p;
18469a747e4fSDavid du Colombier }
18479a747e4fSDavid du Colombier 
18489a747e4fSDavid du Colombier int
_accessok(String * s,int level)1849b27b55e2SDavid du Colombier _accessok(String *s, int level)
18509a747e4fSDavid du Colombier {
18519a747e4fSDavid du Colombier 	Path *p;
18529a747e4fSDavid du Colombier 	char *cp;
1853b27b55e2SDavid du Colombier 	int lvl, offset;
1854b27b55e2SDavid du Colombier 	static char httplogin[] = "/.httplogin";
18559a747e4fSDavid du Colombier 
18569a747e4fSDavid du Colombier 	if(level < 0)
18579a747e4fSDavid du Colombier 		return 1;
18589a747e4fSDavid du Colombier 	lvl = level;
18599a747e4fSDavid du Colombier 	if(lvl >= Maxlevel)
18609a747e4fSDavid du Colombier 		lvl = Maxlevel - 1;
18619a747e4fSDavid du Colombier 
1862b27b55e2SDavid du Colombier 	p = unlinkpath(s_to_c(s), lvl);
18639a747e4fSDavid du Colombier 	if(p->inuse){
18649a747e4fSDavid du Colombier 		/* move to front */
18659a747e4fSDavid du Colombier 		linkpath(p, lvl);
18669a747e4fSDavid du Colombier 		return p->ok;
18679a747e4fSDavid du Colombier 	}
1868b27b55e2SDavid du Colombier 	cp = strrchr(s_to_c(s), '/');
18699a747e4fSDavid du Colombier 	if(cp == nil)
1870b27b55e2SDavid du Colombier 		offset = 0;
1871b27b55e2SDavid du Colombier 	else
1872b27b55e2SDavid du Colombier 		offset = cp - s_to_c(s);
1873b27b55e2SDavid du Colombier 	s_append(s, httplogin);
1874b27b55e2SDavid du Colombier 	if(access(s_to_c(s), AEXIST) == 0){
18759a747e4fSDavid du Colombier 		addpath(p, lvl, 0);
18769a747e4fSDavid du Colombier 		return 0;
18779a747e4fSDavid du Colombier 	}
1878b27b55e2SDavid du Colombier 
1879b27b55e2SDavid du Colombier 	/*
1880b27b55e2SDavid du Colombier 	 * There's no way to shorten a String without
1881b27b55e2SDavid du Colombier 	 * knowing the implementation.
1882b27b55e2SDavid du Colombier 	 */
1883b27b55e2SDavid du Colombier 	s->ptr = s->base+offset;
1884b27b55e2SDavid du Colombier 	s_terminate(s);
1885b27b55e2SDavid du Colombier 	addpath(p, lvl, _accessok(s, level-1));
1886b27b55e2SDavid du Colombier 
18879a747e4fSDavid du Colombier 	return p->ok;
18889a747e4fSDavid du Colombier }
18899a747e4fSDavid du Colombier 
18909a747e4fSDavid du Colombier /*
1891b27b55e2SDavid du Colombier  * check for a subdirectory containing .httplogin
1892b27b55e2SDavid du Colombier  * at each level of the path.
18939a747e4fSDavid du Colombier  */
18949a747e4fSDavid du Colombier int
accessok(char * path)18959a747e4fSDavid du Colombier accessok(char *path)
18969a747e4fSDavid du Colombier {
1897b27b55e2SDavid du Colombier 	int level, r;
18989a747e4fSDavid du Colombier 	char *p;
1899b27b55e2SDavid du Colombier 	String *npath;
19009a747e4fSDavid du Colombier 
1901b27b55e2SDavid du Colombier 	npath = s_copy(path);
1902b27b55e2SDavid du Colombier 	p = s_to_c(npath)+1;
1903b27b55e2SDavid du Colombier 	for(level = 1; level < Maxlevel; level++){
19049a747e4fSDavid du Colombier 		p = strchr(p, '/');
19059a747e4fSDavid du Colombier 		if(p == nil)
19069a747e4fSDavid du Colombier 			break;
19079a747e4fSDavid du Colombier 		p++;
19089a747e4fSDavid du Colombier 	}
1909b27b55e2SDavid du Colombier 
1910b27b55e2SDavid du Colombier 	r = _accessok(npath, level-1);
1911b27b55e2SDavid du Colombier 	s_free(npath);
1912b27b55e2SDavid du Colombier 
1913b27b55e2SDavid du Colombier 	return r;
19149a747e4fSDavid du Colombier }
1915