xref: /plan9/sys/src/cmd/ip/ftpd.c (revision b3994199f6e7550c339372bfdf4b83388358d919)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <ip.h>
6 #include <libsec.h>
7 #include <String.h>
8 
9 #include "glob.h"
10 
11 enum
12 {
13 	/* telnet control character */
14 	Iac=		255,
15 
16 	/* representation types */
17 	Tascii=		0,
18 	Timage=		1,
19 
20 	/* transmission modes */
21 	Mstream=	0,
22 	Mblock=		1,
23 	Mpage=		2,
24 
25 	/* file structure */
26 	Sfile=		0,
27 	Sblock=		1,
28 	Scompressed=	2,
29 
30 	/* read/write buffer size */
31 	Nbuf=		4096,
32 
33 	/* maximum ms we'll wait for a command */
34 	Maxwait=	1000*60*30,		/* inactive for 30 minutes, we hang up */
35 
36 	Maxerr=		128,
37 	Maxpath=	512,
38 };
39 
40 int	abortcmd(char*);
41 int	appendcmd(char*);
42 int	cdupcmd(char*);
43 int	cwdcmd(char*);
44 int	delcmd(char*);
45 int	helpcmd(char*);
46 int	listcmd(char*);
47 int	mdtmcmd(char*);
48 int	mkdircmd(char*);
49 int	modecmd(char*);
50 int	namelistcmd(char*);
51 int	nopcmd(char*);
52 int	passcmd(char*);
53 int	pasvcmd(char*);
54 int	portcmd(char*);
55 int	pwdcmd(char*);
56 int	quitcmd(char*);
57 int	rnfrcmd(char*);
58 int	rntocmd(char*);
59 int	reply(char*, ...);
60 int	restartcmd(char*);
61 int	retrievecmd(char*);
62 int	sitecmd(char*);
63 int	sizecmd(char*);
64 int	storecmd(char*);
65 int	storeucmd(char*);
66 int	structcmd(char*);
67 int	systemcmd(char*);
68 int	typecmd(char*);
69 int	usercmd(char*);
70 
71 int	dialdata(void);
72 char*	abspath(char*);
73 int	crlfwrite(int, char*, int);
74 int	sodoff(void);
75 int	accessok(char*);
76 
77 typedef struct Cmd	Cmd;
78 struct Cmd
79 {
80 	char	*name;
81 	int	(*f)(char*);
82 	int	needlogin;
83 };
84 
85 Cmd cmdtab[] =
86 {
87 	{ "abor",	abortcmd,	0, },
88 	{ "appe",	appendcmd,	1, },
89 	{ "cdup",	cdupcmd,	1, },
90 	{ "cwd",	cwdcmd,		1, },
91 	{ "dele",	delcmd,		1, },
92 	{ "help",	helpcmd,	0, },
93 	{ "list",	listcmd,	1, },
94 	{ "mdtm",	mdtmcmd,	1, },
95 	{ "mkd",	mkdircmd,	1, },
96 	{ "mode",	modecmd,	0, },
97 	{ "nlst",	namelistcmd,	1, },
98 	{ "noop",	nopcmd,		0, },
99 	{ "pass",	passcmd,	0, },
100 	{ "pasv",	pasvcmd,	1, },
101 	{ "pwd",	pwdcmd,		0, },
102 	{ "port", 	portcmd,	1, },
103 	{ "quit",	quitcmd,	0, },
104 	{ "rest",	restartcmd,	1, },
105 	{ "retr",	retrievecmd,	1, },
106 	{ "rmd",	delcmd,		1, },
107 	{ "rnfr",	rnfrcmd,	1, },
108 	{ "rnto",	rntocmd,	1, },
109 	{ "site", sitecmd, 1, },
110 	{ "size", 	sizecmd,	1, },
111 	{ "stor", 	storecmd,	1, },
112 	{ "stou", 	storeucmd,	1, },
113 	{ "stru",	structcmd,	1, },
114 	{ "syst",	systemcmd,	0, },
115 	{ "type", 	typecmd,	0, },
116 	{ "user",	usercmd,	0, },
117 	{ 0, 0, 0 },
118 };
119 
120 #define NONENS "/lib/namespace.ftp"	/* default ns for none */
121 
122 char	user[Maxpath];		/* logged in user */
123 char	curdir[Maxpath];	/* current directory path */
124 Chalstate	*ch;
125 int	loggedin;
126 int	type;			/* transmission type */
127 int	mode;			/* transmission mode */
128 int	structure;		/* file structure */
129 char	data[64];		/* data address */
130 int	pid;			/* transfer process */
131 int	encryption;		/* encryption state */
132 int	isnone, anon_ok, anon_only, anon_everybody;
133 char	cputype[Maxpath];	/* the environment variable of the same name */
134 char	bindir[Maxpath];	/* bin directory for this architecture */
135 char	mailaddr[Maxpath];
136 char	*namespace = NONENS;
137 int	debug;
138 NetConnInfo	*nci;
139 int	createperm = 0660;
140 int	isnoworld;
141 vlong	offset;			/* from restart command */
142 
143 ulong id;
144 
145 typedef struct Passive Passive;
146 struct Passive
147 {
148 	int	inuse;
149 	char	adir[40];
150 	int	afd;
151 	int	port;
152 	uchar	ipaddr[IPaddrlen];
153 } passive;
154 
155 #define FTPLOG "ftp"
156 
157 void
logit(char * fmt,...)158 logit(char *fmt, ...)
159 {
160 	char buf[8192];
161 	va_list arg;
162 	char errstr[128];
163 
164 	rerrstr(errstr, sizeof errstr);
165 	va_start(arg, fmt);
166 	vseprint(buf, buf+sizeof(buf), fmt, arg);
167 	va_end(arg);
168 	syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf);
169 	werrstr(errstr, sizeof errstr);
170 }
171 
172 static void
usage(void)173 usage(void)
174 {
175 	syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0);
176 	fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0);
177 	exits("usage");
178 }
179 
180 /*
181  *  read commands from the control stream and dispatch
182  */
183 void
main(int argc,char ** argv)184 main(int argc, char **argv)
185 {
186 	char *cmd;
187 	char *arg;
188 	char *p;
189 	Cmd *t;
190 	Biobuf in;
191 	int i;
192 
193 	ARGBEGIN{
194 	case 'a':		/* anonymous OK */
195 		anon_ok = 1;
196 		break;
197 	case 'A':
198 		anon_ok = 1;
199 		anon_only = 1;
200 		break;
201 	case 'd':
202 		debug++;
203 		break;
204 	case 'e':
205 		anon_ok = 1;
206 		anon_everybody = 1;
207 		break;
208 	case 'n':
209 		namespace = EARGF(usage());
210 		break;
211 	default:
212 		usage();
213 	}ARGEND
214 
215 	/* open log file before doing a newns */
216 	syslog(0, FTPLOG, nil);
217 
218 	/* find out who is calling */
219 	if(argc < 1)
220 		nci = getnetconninfo(nil, 0);
221 	else
222 		nci = getnetconninfo(argv[argc-1], 0);
223 	if(nci == nil)
224 		sysfatal("ftpd needs a network address");
225 
226 	strcpy(mailaddr, "?");
227 	id = getpid();
228 
229 	/* figure out which binaries to bind in later (only for none) */
230 	arg = getenv("cputype");
231 	if(arg)
232 		strecpy(cputype, cputype+sizeof cputype, arg);
233 	else
234 		strcpy(cputype, "mips");
235 	/* shurely /%s/bin */
236 	snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype);
237 
238 	Binit(&in, 0, OREAD);
239 	reply("220 Plan 9 FTP server ready");
240 	alarm(Maxwait);
241 	while(cmd = Brdline(&in, '\n')){
242 		alarm(0);
243 
244 		/*
245 		 *  strip out trailing cr's & lf and delimit with null
246 		 */
247 		i = Blinelen(&in)-1;
248 		cmd[i] = 0;
249 		if(debug)
250 			logit("%s", cmd);
251 		while(i > 0 && cmd[i-1] == '\r')
252 			cmd[--i] = 0;
253 
254 		/*
255 		 *  hack for GatorFTP+, look for a 0x10 used as a delimiter
256 		 */
257 		p = strchr(cmd, 0x10);
258 		if(p)
259 			*p = 0;
260 
261 		/*
262 		 *  get rid of telnet control sequences (we don't need them)
263 		 */
264 		while(*cmd && (uchar)*cmd == Iac){
265 			cmd++;
266 			if(*cmd)
267 				cmd++;
268 		}
269 
270 		/*
271 		 *  parse the message (command arg)
272 		 */
273 		arg = strchr(cmd, ' ');
274 		if(arg){
275 			*arg++ = 0;
276 			while(*arg == ' ')
277 				arg++;
278 		}
279 
280 		/*
281 		 *  ignore blank commands
282 		 */
283 		if(*cmd == 0)
284 			continue;
285 
286 		/*
287 		 *  lookup the command and do it
288 		 */
289 		for(p = cmd; *p; p++)
290 			*p = tolower(*p);
291 		for(t = cmdtab; t->name; t++)
292 			if(strcmp(cmd, t->name) == 0){
293 				if(t->needlogin && !loggedin)
294 					sodoff();
295 				else if((*t->f)(arg) < 0)
296 					exits(0);
297 				break;
298 			}
299 		if(t->f != restartcmd){
300 			/*
301 			 *  the file offset is set to zero following
302 			 *  all commands except the restart command
303 			 */
304 			offset = 0;
305 		}
306 		if(t->name == 0){
307 			/*
308 			 *  the OOB bytes preceding an abort from UCB machines
309 			 *  comes out as something unrecognizable instead of
310 			 *  IAC's.  Certainly a Plan 9 bug but I can't find it.
311 			 *  This is a major hack to avoid the problem. -- presotto
312 			 */
313 			i = strlen(cmd);
314 			if(i > 4 && strcmp(cmd+i-4, "abor") == 0){
315 				abortcmd(0);
316 			} else{
317 				logit("%s (%s) command not implemented", cmd, arg?arg:"");
318 				reply("502 %s command not implemented", cmd);
319 			}
320 		}
321 		alarm(Maxwait);
322 	}
323 	if(pid)
324 		postnote(PNPROC, pid, "kill");
325 }
326 
327 /*
328  *  reply to a command
329  */
330 int
reply(char * fmt,...)331 reply(char *fmt, ...)
332 {
333 	va_list arg;
334 	char buf[8192], *s;
335 
336 	va_start(arg, fmt);
337 	s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg);
338 	va_end(arg);
339 	if(debug){
340 		*s = 0;
341 		logit("%s", buf);
342 	}
343 	*s++ = '\r';
344 	*s++ = '\n';
345 	write(1, buf, s - buf);
346 	return 0;
347 }
348 
349 int
sodoff(void)350 sodoff(void)
351 {
352 	return reply("530 Sod off, service requires login");
353 }
354 
355 /*
356  *  run a command in a separate process
357  */
358 int
asproc(void (* f)(char *,int),char * arg,int arg2)359 asproc(void (*f)(char*, int), char *arg, int arg2)
360 {
361 	int i;
362 
363 	if(pid){
364 		/* wait for previous command to finish */
365 		for(;;){
366 			i = waitpid();
367 			if(i == pid || i < 0)
368 				break;
369 		}
370 	}
371 
372 	switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
373 	case -1:
374 		return reply("450 Out of processes: %r");
375 	case 0:
376 		(*f)(arg, arg2);
377 		exits(0);
378 	default:
379 		break;
380 	}
381 	return 0;
382 }
383 
384 /*
385  * run a command to filter a tail
386  */
387 int
transfer(char * cmd,char * a1,char * a2,char * a3,int image)388 transfer(char *cmd, char *a1, char *a2, char *a3, int image)
389 {
390 	int n, dfd, fd, bytes, eofs, pid;
391 	int pfd[2];
392 	char buf[Nbuf], *p;
393 	Waitmsg *w;
394 
395 	reply("150 Opening data connection for %s (%s)", cmd, data);
396 	dfd = dialdata();
397 	if(dfd < 0)
398 		return reply("425 Error opening data connection: %r");
399 
400 	if(pipe(pfd) < 0)
401 		return reply("520 Internal Error: %r");
402 
403 	bytes = 0;
404 	switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){
405 	case -1:
406 		return reply("450 Out of processes: %r");
407 	case 0:
408 		logit("running %s %s %s %s pid %d",
409 			cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid());
410 		close(pfd[1]);
411 		close(dfd);
412 		dup(pfd[0], 1);
413 		dup(pfd[0], 2);
414 		if(isnone){
415 			fd = open("#s/boot", ORDWR);
416 			if(fd < 0
417 			|| bind("#/", "/", MAFTER) < 0
418 			|| amount(fd, "/bin", MREPL, "") < 0
419 			|| bind("#c", "/dev", MAFTER) < 0
420 			|| bind(bindir, "/bin", MREPL) < 0)
421 				exits("building name space");
422 			close(fd);
423 		}
424 		execl(cmd, cmd, a1, a2, a3, nil);
425 		exits(cmd);
426 	default:
427 		close(pfd[0]);
428 		eofs = 0;
429 		while((n = read(pfd[1], buf, sizeof buf)) >= 0){
430 			if(n == 0){
431 				if(eofs++ > 5)
432 					break;
433 				else
434 					continue;
435 			}
436 			eofs = 0;
437 			p = buf;
438 			if(offset > 0){
439 				if(n > offset){
440 					p = buf+offset;
441 					n -= offset;
442 					offset = 0;
443 				} else {
444 					offset -= n;
445 					continue;
446 				}
447 			}
448 			if(!image)
449 				n = crlfwrite(dfd, p, n);
450 			else
451 				n = write(dfd, p, n);
452 			if(n < 0){
453 				postnote(PNPROC, pid, "kill");
454 				bytes = -1;
455 				break;
456 			}
457 			bytes += n;
458 		}
459 		close(pfd[1]);
460 		close(dfd);
461 		break;
462 	}
463 
464 	/* wait for this command to finish */
465 	for(;;){
466 		w = wait();
467 		if(w == nil || w->pid == pid)
468 			break;
469 		free(w);
470 	}
471 	if(w != nil && w->msg != nil && w->msg[0] != 0){
472 		bytes = -1;
473 		logit("%s", w->msg);
474 		logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg);
475 	}
476 	free(w);
477 	reply("226 Transfer complete");
478 	return bytes;
479 }
480 
481 
482 /*
483  *  just reply OK
484  */
485 int
nopcmd(char * arg)486 nopcmd(char *arg)
487 {
488 	USED(arg);
489 	reply("510 Plan 9 FTP daemon still alive");
490 	return 0;
491 }
492 
493 /*
494  *  login as user
495  */
496 int
loginuser(char * user,char * nsfile,int gotoslash)497 loginuser(char *user, char *nsfile, int gotoslash)
498 {
499 	logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile);
500 	if(nsfile != nil && newns(user, nsfile) < 0){
501 		logit("namespace file %s does not exist", nsfile);
502 		return reply("530 Not logged in: login out of service");
503 	}
504 	getwd(curdir, sizeof(curdir));
505 	if(gotoslash){
506 		chdir("/");
507 		strcpy(curdir, "/");
508 	}
509 	putenv("service", "ftp");
510 	loggedin = 1;
511 	if(debug == 0)
512 		reply("230- If you have problems, send mail to 'postmaster'.");
513 	return reply("230 Logged in");
514 }
515 
516 static void
slowdown(void)517 slowdown(void)
518 {
519 	static ulong pause;
520 
521 	if (pause) {
522 		sleep(pause);			/* deter guessers */
523 		if (pause < (1UL << 20))
524 			pause *= 2;
525 	} else
526 		pause = 1000;
527 }
528 
529 /*
530  *  get a user id, reply with a challenge.  The users 'anonymous'
531  *  and 'ftp' are equivalent to 'none'.  The user 'none' requires
532  *  no challenge.
533  */
534 int
usercmd(char * name)535 usercmd(char *name)
536 {
537 	slowdown();
538 
539 	logit("user %s %s", name, nci->rsys);
540 	if(loggedin)
541 		return reply("530 Already logged in as %s", user);
542 	if(name == 0 || *name == 0)
543 		return reply("530 user command needs user name");
544 	isnoworld = 0;
545 	if(*name == ':'){
546 		debug = 1;
547 		name++;
548 	}
549 	strncpy(user, name, sizeof(user));
550 	if(debug)
551 		logit("debugging");
552 	user[sizeof(user)-1] = 0;
553 	if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0)
554 		strcpy(user, "none");
555 	else if(anon_everybody)
556 		strcpy(user,"none");
557 
558 	if(strcmp(user, "Administrator") == 0 || strcmp(user, "admin") == 0)
559 		return reply("530 go away, script kiddie");
560 	else if(strcmp(user, "*none") == 0){
561 		if(!anon_ok)
562 			return reply("530 Not logged in: anonymous disallowed");
563 		return loginuser("none", namespace, 1);
564 	}
565 	else if(strcmp(user, "none") == 0){
566 		if(!anon_ok)
567 			return reply("530 Not logged in: anonymous disallowed");
568 		return reply("331 Send email address as password");
569 	}
570 	else if(anon_only)
571 		return reply("530 Not logged in: anonymous access only");
572 
573 	isnoworld = noworld(name);
574 	if(isnoworld)
575 		return reply("331 OK");
576 
577 	/* consult the auth server */
578 	if(ch)
579 		auth_freechal(ch);
580 	if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
581 		return reply("421 %r");
582 	return reply("331 encrypt challenge, %s, as a password", ch->chal);
583 }
584 
585 /*
586  *  get a password, set up user if it works.
587  */
588 int
passcmd(char * response)589 passcmd(char *response)
590 {
591 	char namefile[128];
592 	AuthInfo *ai;
593 
594 	if(response == nil)
595 		response = "";
596 
597 	if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){
598 		/* for none, accept anything as a password */
599 		isnone = 1;
600 		strncpy(mailaddr, response, sizeof(mailaddr)-1);
601 		return loginuser("none", namespace, 1);
602 	}
603 
604 	if(isnoworld){
605 		/* noworld gets a password in the clear */
606 		if(login(user, response, "/lib/namespace.noworld") < 0)
607 			return reply("530 Not logged in");
608 		createperm = 0664;
609 		/* login has already setup the namespace */
610 		return loginuser(user, nil, 0);
611 	} else {
612 		/* for everyone else, do challenge response */
613 		if(ch == nil)
614 			return reply("531 Send user id before encrypted challenge");
615 		ch->resp = response;
616 		ch->nresp = strlen(response);
617 		ai = auth_response(ch);
618 		if(ai == nil || auth_chuid(ai, nil) < 0) {
619 			slowdown();
620 			return reply("530 Not logged in: %r");
621 		}
622 		auth_freechal(ch);
623 		ch = nil;
624 
625 		/* if the user has specified a namespace for ftp, use it */
626 		snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user);
627 		strcpy(mailaddr, user);
628 		createperm = 0660;
629 		if(access(namefile, 0) == 0)
630 			return loginuser(user, namefile, 0);
631 		else
632 			return loginuser(user, "/lib/namespace", 0);
633 	}
634 }
635 
636 /*
637  *  print working directory
638  */
639 int
pwdcmd(char * arg)640 pwdcmd(char *arg)
641 {
642 	if(arg)
643 		return reply("550 Pwd takes no argument");
644 	return reply("257 \"%s\" is the current directory", curdir);
645 }
646 
647 /*
648  *  chdir
649  */
650 int
cwdcmd(char * dir)651 cwdcmd(char *dir)
652 {
653 	char *rp;
654 	char buf[Maxpath];
655 
656 	/* shell cd semantics */
657 	if(dir == 0 || *dir == 0){
658 		if(isnone)
659 			rp = "/";
660 		else {
661 			snprint(buf, sizeof buf, "/usr/%s", user);
662 			rp = buf;
663 		}
664 		if(accessok(rp) == 0)
665 			rp = nil;
666 	} else
667 		rp = abspath(dir);
668 
669 	if(rp == nil)
670 		return reply("550 Permission denied");
671 
672 	if(chdir(rp) < 0)
673 		return reply("550 Cwd failed: %r");
674 	strcpy(curdir, rp);
675 	return reply("250 directory changed to %s", curdir);
676 }
677 
678 /*
679  *  chdir ..
680  */
681 int
cdupcmd(char * dp)682 cdupcmd(char *dp)
683 {
684 	USED(dp);
685 	return cwdcmd("..");
686 }
687 
688 int
quitcmd(char * arg)689 quitcmd(char *arg)
690 {
691 	USED(arg);
692 	reply("200 Bye");
693 	if(pid)
694 		postnote(PNPROC, pid, "kill");
695 	return -1;
696 }
697 
698 int
typecmd(char * arg)699 typecmd(char *arg)
700 {
701 	int c;
702 	char *x;
703 
704 	x = arg;
705 	if(arg == 0)
706 		return reply("501 Type command needs arguments");
707 
708 	while(c = *arg++){
709 		switch(tolower(c)){
710 		case 'a':
711 			type = Tascii;
712 			break;
713 		case 'i':
714 		case 'l':
715 			type = Timage;
716 			break;
717 		case '8':
718 		case ' ':
719 		case 'n':
720 		case 't':
721 		case 'c':
722 			break;
723 		default:
724 			return reply("501 Unimplemented type %s", x);
725 		}
726 	}
727 	return reply("200 Type %s", type==Tascii ? "Ascii" : "Image");
728 }
729 
730 int
modecmd(char * arg)731 modecmd(char *arg)
732 {
733 	if(arg == 0)
734 		return reply("501 Mode command needs arguments");
735 	while(*arg){
736 		switch(tolower(*arg)){
737 		case 's':
738 			mode = Mstream;
739 			break;
740 		default:
741 			return reply("501 Unimplemented mode %c", *arg);
742 		}
743 		arg++;
744 	}
745 	return reply("200 Stream mode");
746 }
747 
748 int
structcmd(char * arg)749 structcmd(char *arg)
750 {
751 	if(arg == 0)
752 		return reply("501 Struct command needs arguments");
753 	for(; *arg; arg++){
754 		switch(tolower(*arg)){
755 		case 'f':
756 			structure = Sfile;
757 			break;
758 		default:
759 			return reply("501 Unimplemented structure %c", *arg);
760 		}
761 	}
762 	return reply("200 File structure");
763 }
764 
765 int
portcmd(char * arg)766 portcmd(char *arg)
767 {
768 	char *field[7];
769 	int n;
770 
771 	if(arg == 0)
772 		return reply("501 Port command needs arguments");
773 	n = getfields(arg, field, 7, 0, ", ");
774 	if(n != 6)
775 		return reply("501 Incorrect port specification");
776 	snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2],
777 		field[3], atoi(field[4])*256 + atoi(field[5]));
778 	return reply("200 Data port is %s", data);
779 }
780 
781 int
mountnet(void)782 mountnet(void)
783 {
784 	int rv;
785 
786 	rv = 0;
787 
788 	if(bind("#/", "/", MAFTER) < 0){
789 		logit("can't bind #/ to /: %r");
790 		return reply("500 can't bind #/ to /: %r");
791 	}
792 
793 	if(bind(nci->spec, "/net", MBEFORE) < 0){
794 		logit("can't bind %s to /net: %r", nci->spec);
795 		rv = reply("500 can't bind %s to /net: %r", nci->spec);
796 		unmount("#/", "/");
797 	}
798 
799 	return rv;
800 }
801 
802 void
unmountnet(void)803 unmountnet(void)
804 {
805 	unmount(0, "/net");
806 	unmount("#/", "/");
807 }
808 
809 int
pasvcmd(char * arg)810 pasvcmd(char *arg)
811 {
812 	NetConnInfo *nnci;
813 	Passive *p;
814 
815 	USED(arg);
816 	p = &passive;
817 
818 	if(p->inuse){
819 		close(p->afd);
820 		p->inuse = 0;
821 	}
822 
823 	if(mountnet() < 0)
824 		return 0;
825 
826 	p->afd = announce("tcp!*!0", passive.adir);
827 	if(p->afd < 0){
828 		unmountnet();
829 		return reply("500 No free ports");
830 	}
831 	nnci = getnetconninfo(p->adir, -1);
832 	unmountnet();
833 
834 	/* parse the local address */
835 	if(debug)
836 		logit("local sys is %s", nci->lsys);
837 	parseip(p->ipaddr, nci->lsys);
838 	if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0)
839 		parseip(p->ipaddr, nci->lsys);
840 	p->port = atoi(nnci->lserv);
841 
842 	freenetconninfo(nnci);
843 	p->inuse = 1;
844 
845 	return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
846 		p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3],
847 		p->port>>8, p->port&0xff);
848 }
849 
850 enum
851 {
852 	Narg=32,
853 };
854 int Cflag, rflag, tflag, Rflag;
855 int maxnamelen;
856 int col;
857 
858 char*
mode2asc(int m)859 mode2asc(int m)
860 {
861 	static char asc[12];
862 	char *p;
863 
864 	strcpy(asc, "----------");
865 	if(DMDIR & m)
866 		asc[0] = 'd';
867 	if(DMAPPEND & m)
868 		asc[0] = 'a';
869 	else if(DMEXCL & m)
870 		asc[3] = 'l';
871 
872 	for(p = asc+1; p < asc + 10; p += 3, m<<=3){
873 		if(m & 0400)
874 			p[0] = 'r';
875 		if(m & 0200)
876 			p[1] = 'w';
877 		if(m & 0100)
878 			p[2] = 'x';
879 	}
880 	return asc;
881 }
882 void
listfile(Biobufhdr * b,char * name,int lflag,char * dname)883 listfile(Biobufhdr *b, char *name, int lflag, char *dname)
884 {
885 	char ts[32];
886 	int n, links, pad;
887 	long now;
888 	char *x;
889 	Dir *d;
890 
891 	x = abspath(name);
892 	if(x == nil)
893 		return;
894 	d = dirstat(x);
895 	if(d == nil)
896 		return;
897 	if(isnone){
898 		if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0)
899 			d->mode &= ~0222;
900 		d->uid = "none";
901 		d->gid = "none";
902 	}
903 
904 	strcpy(ts, ctime(d->mtime));
905 	ts[16] = 0;
906 	now = time(0);
907 	if(now - d->mtime > 6*30*24*60*60)
908 		memmove(ts+11, ts+23, 5);
909 	if(lflag){
910 		/* Unix style long listing */
911 		if(DMDIR&d->mode){
912 			links = 2;
913 			d->length = 512;
914 		} else
915 			links = 1;
916 
917 		Bprint(b, "%s %3d %-8s %-8s %7lld %s ",
918 			mode2asc(d->mode), links,
919 			d->uid, d->gid, d->length, ts+4);
920 	}
921 	if(Cflag && maxnamelen < 40){
922 		n = strlen(name);
923 		pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1);
924 		if(pad+maxnamelen+1 < 60){
925 			Bprint(b, "%*s", pad-col+n, name);
926 			col = pad+n;
927 		}
928 		else{
929 			Bprint(b, "\r\n%s", name);
930 			col = n;
931 		}
932 	}
933 	else{
934 		if(dname)
935 			Bprint(b, "%s/", dname);
936 		Bprint(b, "%s\r\n", name);
937 	}
938 	free(d);
939 }
940 int
dircomp(void * va,void * vb)941 dircomp(void *va, void *vb)
942 {
943 	int rv;
944 	Dir *a, *b;
945 
946 	a = va;
947 	b = vb;
948 
949 	if(tflag)
950 		rv = b->mtime - a->mtime;
951 	else
952 		rv = strcmp(a->name, b->name);
953 	return (rflag?-1:1)*rv;
954 }
955 void
listdir(char * name,Biobufhdr * b,int lflag,int * printname,Globlist * gl)956 listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl)
957 {
958 	Dir *p;
959 	int fd, n, i, l;
960 	char *dname;
961 	uvlong total;
962 
963 	col = 0;
964 
965 	fd = open(name, OREAD);
966 	if(fd < 0){
967 		Bprint(b, "can't read %s: %r\r\n", name);
968 		return;
969 	}
970 	dname = 0;
971 	if(*printname){
972 		if(Rflag || lflag)
973 			Bprint(b, "\r\n%s:\r\n", name);
974 		else
975 			dname = name;
976 	}
977 	n = dirreadall(fd, &p);
978 	close(fd);
979 	if(Cflag){
980 		for(i = 0; i < n; i++){
981 			l = strlen(p[i].name);
982 			if(l > maxnamelen)
983 				maxnamelen = l;
984 		}
985 	}
986 
987 	/* Unix style total line */
988 	if(lflag){
989 		total = 0;
990 		for(i = 0; i < n; i++){
991 			if(p[i].qid.type & QTDIR)
992 				total += 512;
993 			else
994 				total += p[i].length;
995 		}
996 		Bprint(b, "total %ulld\r\n", total/512);
997 	}
998 
999 	qsort(p, n, sizeof(Dir), dircomp);
1000 	for(i = 0; i < n; i++){
1001 		if(Rflag && (p[i].qid.type & QTDIR)){
1002 			*printname = 1;
1003 			globadd(gl, name, p[i].name);
1004 		}
1005 		listfile(b, p[i].name, lflag, dname);
1006 	}
1007 	free(p);
1008 }
1009 void
list(char * arg,int lflag)1010 list(char *arg, int lflag)
1011 {
1012 	Dir *d;
1013 	Globlist *gl;
1014 	Glob *g;
1015 	int dfd, printname;
1016 	int i, n, argc;
1017 	char *alist[Narg];
1018 	char **argv;
1019 	Biobufhdr bh;
1020 	uchar buf[512];
1021 	char *p, *s;
1022 
1023 	if(arg == 0)
1024 		arg = "";
1025 
1026 	if(debug)
1027 		logit("ls %s (. = %s)", arg, curdir);
1028 
1029 	/* process arguments, understand /bin/ls -l option */
1030 	argv = alist;
1031 	argv[0] = "/bin/ls";
1032 	argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1;
1033 	argv[argc] = 0;
1034 	rflag = 0;
1035 	tflag = 0;
1036 	Rflag = 0;
1037 	Cflag = 0;
1038 	col = 0;
1039 	ARGBEGIN{
1040 	case 'l':
1041 		lflag++;
1042 		break;
1043 	case 'R':
1044 		Rflag++;
1045 		break;
1046 	case 'C':
1047 		Cflag++;
1048 		break;
1049 	case 'r':
1050 		rflag++;
1051 		break;
1052 	case 't':
1053 		tflag++;
1054 		break;
1055 	}ARGEND;
1056 	if(Cflag)
1057 		lflag = 0;
1058 
1059 	dfd = dialdata();
1060 	if(dfd < 0){
1061 		reply("425 Error opening data connection:%r");
1062 		return;
1063 	}
1064 	reply("150 Opened data connection (%s)", data);
1065 
1066 	Binits(&bh, dfd, OWRITE, buf, sizeof(buf));
1067 	if(argc == 0){
1068 		argc = 1;
1069 		argv = alist;
1070 		argv[0] = ".";
1071 	}
1072 
1073 	for(i = 0; i < argc; i++){
1074 		chdir(curdir);
1075 		gl = glob(argv[i]);
1076 		if(gl == nil)
1077 			continue;
1078 
1079 		printname = gl->first != nil && gl->first->next != nil;
1080 		maxnamelen = 8;
1081 
1082 		if(Cflag)
1083 			for(g = gl->first; g; g = g->next)
1084 				if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen)
1085 					maxnamelen = n;
1086 		while(s = globiter(gl)){
1087 			if(debug)
1088 				logit("glob %s", s);
1089 			p = abspath(s);
1090 			if(p == nil){
1091 				free(s);
1092 				continue;
1093 			}
1094 			d = dirstat(p);
1095 			if(d == nil){
1096 				free(s);
1097 				continue;
1098 			}
1099 			if(d->qid.type & QTDIR)
1100 				listdir(s, &bh, lflag, &printname, gl);
1101 			else
1102 				listfile(&bh, s, lflag, 0);
1103 			free(s);
1104 			free(d);
1105 		}
1106 		globlistfree(gl);
1107 	}
1108 	if(Cflag)
1109 		Bprint(&bh, "\r\n");
1110 	Bflush(&bh);
1111 	close(dfd);
1112 
1113 	reply("226 Transfer complete (list %s)", arg);
1114 }
1115 int
namelistcmd(char * arg)1116 namelistcmd(char *arg)
1117 {
1118 	return asproc(list, arg, 0);
1119 }
1120 int
listcmd(char * arg)1121 listcmd(char *arg)
1122 {
1123 	return asproc(list, arg, 1);
1124 }
1125 
1126 /*
1127  * fuse compatability
1128  */
1129 int
oksiteuser(void)1130 oksiteuser(void)
1131 {
1132 	char buf[64];
1133 	int fd, n;
1134 
1135 	fd = open("#c/user", OREAD);
1136 	if(fd < 0)
1137 		return 1;
1138 	n = read(fd, buf, sizeof buf - 1);
1139 	if(n > 0){
1140 		buf[n] = 0;
1141 		if(strcmp(buf, "none") == 0)
1142 			n = -1;
1143 	}
1144 	close(fd);
1145 	return n > 0;
1146 }
1147 
1148 int
sitecmd(char * arg)1149 sitecmd(char *arg)
1150 {
1151 	char *f[4];
1152 	int nf, r;
1153 	Dir *d;
1154 
1155 	if(arg == 0)
1156 		return reply("501 bad site command");
1157 	nf = tokenize(arg, f, nelem(f));
1158 	if(nf != 3 || cistrcmp(f[0], "chmod") != 0)
1159 		return reply("501 bad site command");
1160 	if(!oksiteuser())
1161 		return reply("550 Permission denied");
1162 	d = dirstat(f[2]);
1163 	if(d == nil)
1164 		return reply("501 site chmod: file does not exist");
1165 	d->mode &= ~0777;
1166 	d->mode |= strtoul(f[1], 0, 8) & 0777;
1167 	r = dirwstat(f[2], d);
1168 	free(d);
1169 	if(r < 0)
1170 		return reply("550 Permission denied %r");
1171 	return reply("200 very well, then");
1172  }
1173 
1174 /*
1175  *  return the size of the file
1176  */
1177 int
sizecmd(char * arg)1178 sizecmd(char *arg)
1179 {
1180 	Dir *d;
1181 	int rv;
1182 
1183 	if(arg == 0)
1184 		return reply("501 Size command requires pathname");
1185 	arg = abspath(arg);
1186 	d = dirstat(arg);
1187 	if(d == nil)
1188 		return reply("501 %r accessing %s", arg);
1189 	rv = reply("213 %lld", d->length);
1190 	free(d);
1191 	return rv;
1192 }
1193 
1194 /*
1195  *  return the modify time of the file
1196  */
1197 int
mdtmcmd(char * arg)1198 mdtmcmd(char *arg)
1199 {
1200 	Dir *d;
1201 	Tm *t;
1202 	int rv;
1203 
1204 	if(arg == 0)
1205 		return reply("501 Mdtm command requires pathname");
1206 	if(arg == 0)
1207 		return reply("550 Permission denied");
1208 	d = dirstat(arg);
1209 	if(d == nil)
1210 		return reply("501 %r accessing %s", arg);
1211 	t = gmtime(d->mtime);
1212 	rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
1213 			t->year+1900, t->mon+1, t->mday,
1214 			t->hour, t->min, t->sec);
1215 	free(d);
1216 	return rv;
1217 }
1218 
1219 /*
1220  *  set an offset to start reading a file from
1221  *  only lasts for one command
1222  */
1223 int
restartcmd(char * arg)1224 restartcmd(char *arg)
1225 {
1226 	if(arg == 0)
1227 		return reply("501 Restart command requires offset");
1228 	offset = atoll(arg);
1229 	if(offset < 0){
1230 		offset = 0;
1231 		return reply("501 Bad offset");
1232 	}
1233 
1234 	return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset);
1235 }
1236 
1237 /*
1238  *  send a file to the user
1239  */
1240 int
crlfwrite(int fd,char * p,int n)1241 crlfwrite(int fd, char *p, int n)
1242 {
1243 	char *ep, *np;
1244 	char buf[2*Nbuf];
1245 
1246 	for(np = buf, ep = p + n; p < ep; p++){
1247 		if(*p == '\n')
1248 			*np++ = '\r';
1249 		*np++ = *p;
1250 	}
1251 	if(write(fd, buf, np - buf) == np - buf)
1252 		return n;
1253 	else
1254 		return -1;
1255 }
1256 void
retrievedir(char * arg)1257 retrievedir(char *arg)
1258 {
1259 	int n;
1260 	char *p;
1261 	String *file;
1262 
1263 	if(type != Timage){
1264 		reply("550 This file requires type binary/image");
1265 		return;
1266 	}
1267 
1268 	file = s_copy(arg);
1269 	p = strrchr(s_to_c(file), '/');
1270 	if(p != s_to_c(file)){
1271 		*p++ = 0;
1272 		chdir(s_to_c(file));
1273 	} else {
1274 		chdir("/");
1275 		p = s_to_c(file)+1;
1276 	}
1277 
1278 	n = transfer("/bin/tar", "c", p, 0, 1);
1279 	if(n < 0)
1280 		logit("get %s failed", arg);
1281 	else
1282 		logit("get %s OK %d", arg, n);
1283 	s_free(file);
1284 }
1285 void
retrieve(char * arg,int arg2)1286 retrieve(char *arg, int arg2)
1287 {
1288 	int dfd, fd, n, i, bytes;
1289 	Dir *d;
1290 	char buf[Nbuf];
1291 	char *p, *ep;
1292 
1293 	USED(arg2);
1294 
1295 	p = strchr(arg, '\r');
1296 	if(p){
1297 		logit("cr in file name", arg);
1298 		*p = 0;
1299 	}
1300 
1301 	fd = open(arg, OREAD);
1302 	if(fd == -1){
1303 		n = strlen(arg);
1304 		if(n > 4 && strcmp(arg+n-4, ".tar") == 0){
1305 			*(arg+n-4) = 0;
1306 			d = dirstat(arg);
1307 			if(d != nil){
1308 				if(d->qid.type & QTDIR){
1309 					retrievedir(arg);
1310 					free(d);
1311 					return;
1312 				}
1313 				free(d);
1314 			}
1315 		}
1316 		logit("get %s failed", arg);
1317 		reply("550 Error opening %s: %r", arg);
1318 		return;
1319 	}
1320 	if(offset != 0)
1321 		if(seek(fd, offset, 0) < 0){
1322 			reply("550 %s: seek to %lld failed", arg, offset);
1323 			close(fd);
1324 			return;
1325 		}
1326 	d = dirfstat(fd);
1327 	if(d != nil){
1328 		if(d->qid.type & QTDIR){
1329 			reply("550 %s: not a plain file.", arg);
1330 			close(fd);
1331 			free(d);
1332 			return;
1333 		}
1334 		free(d);
1335 	}
1336 
1337 	n = read(fd, buf, sizeof(buf));
1338 	if(n < 0){
1339 		logit("get %s failed", arg, mailaddr, nci->rsys);
1340 		reply("550 Error reading %s: %r", arg);
1341 		close(fd);
1342 		return;
1343 	}
1344 
1345 	if(type != Timage)
1346 		for(p = buf, ep = &buf[n]; p < ep; p++)
1347 			if(*p & 0x80){
1348 				close(fd);
1349 				reply("550 This file requires type binary/image");
1350 				return;
1351 			}
1352 
1353 	reply("150 Opening data connection for %s (%s)", arg, data);
1354 	dfd = dialdata();
1355 	if(dfd < 0){
1356 		reply("425 Error opening data connection:%r");
1357 		close(fd);
1358 		return;
1359 	}
1360 
1361 	bytes = 0;
1362 	do {
1363 		switch(type){
1364 		case Timage:
1365 			i = write(dfd, buf, n);
1366 			break;
1367 		default:
1368 			i = crlfwrite(dfd, buf, n);
1369 			break;
1370 		}
1371 		if(i != n){
1372 			close(fd);
1373 			close(dfd);
1374 			logit("get %s %r to data connection after %d", arg, bytes);
1375 			reply("550 Error writing to data connection: %r");
1376 			return;
1377 		}
1378 		bytes += n;
1379 	} while((n = read(fd, buf, sizeof(buf))) > 0);
1380 
1381 	if(n < 0)
1382 		logit("get %s %r after %d", arg, bytes);
1383 
1384 	close(fd);
1385 	close(dfd);
1386 	reply("226 Transfer complete");
1387 	logit("get %s OK %d", arg, bytes);
1388 }
1389 int
retrievecmd(char * arg)1390 retrievecmd(char *arg)
1391 {
1392 	if(arg == 0)
1393 		return reply("501 Retrieve command requires an argument");
1394 	arg = abspath(arg);
1395 	if(arg == 0)
1396 		return reply("550 Permission denied");
1397 
1398 	return asproc(retrieve, arg, 0);
1399 }
1400 
1401 /*
1402  *  get a file from the user
1403  */
1404 int
lfwrite(int fd,char * p,int n)1405 lfwrite(int fd, char *p, int n)
1406 {
1407 	char *ep, *np;
1408 	char buf[Nbuf];
1409 
1410 	for(np = buf, ep = p + n; p < ep; p++){
1411 		if(*p != '\r')
1412 			*np++ = *p;
1413 	}
1414 	if(write(fd, buf, np - buf) == np - buf)
1415 		return n;
1416 	else
1417 		return -1;
1418 }
1419 void
store(char * arg,int fd)1420 store(char *arg, int fd)
1421 {
1422 	int dfd, n, i;
1423 	char buf[Nbuf];
1424 
1425 	reply("150 Opening data connection for %s (%s)", arg, data);
1426 	dfd = dialdata();
1427 	if(dfd < 0){
1428 		reply("425 Error opening data connection:%r");
1429 		close(fd);
1430 		return;
1431 	}
1432 
1433 	while((n = read(dfd, buf, sizeof(buf))) > 0){
1434 		switch(type){
1435 		case Timage:
1436 			i = write(fd, buf, n);
1437 			break;
1438 		default:
1439 			i = lfwrite(fd, buf, n);
1440 			break;
1441 		}
1442 		if(i != n){
1443 			close(fd);
1444 			close(dfd);
1445 			reply("550 Error writing file");
1446 			return;
1447 		}
1448 	}
1449 	close(fd);
1450 	close(dfd);
1451 	logit("put %s OK", arg);
1452 	reply("226 Transfer complete");
1453 }
1454 int
storecmd(char * arg)1455 storecmd(char *arg)
1456 {
1457 	int fd, rv;
1458 
1459 	if(arg == 0)
1460 		return reply("501 Store command requires an argument");
1461 	arg = abspath(arg);
1462 	if(arg == 0)
1463 		return reply("550 Permission denied");
1464 	if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1))
1465 		return reply("550 Permission denied");
1466 	if(offset){
1467 		fd = open(arg, OWRITE);
1468 		if(fd == -1)
1469 			return reply("550 Error opening %s: %r", arg);
1470 		if(seek(fd, offset, 0) == -1)
1471 			return reply("550 Error seeking %s to %d: %r",
1472 				arg, offset);
1473 	} else {
1474 		fd = create(arg, OWRITE, createperm);
1475 		if(fd == -1)
1476 			return reply("550 Error creating %s: %r", arg);
1477 	}
1478 
1479 	rv = asproc(store, arg, fd);
1480 	close(fd);
1481 	return rv;
1482 }
1483 int
appendcmd(char * arg)1484 appendcmd(char *arg)
1485 {
1486 	int fd, rv;
1487 
1488 	if(arg == 0)
1489 		return reply("501 Append command requires an argument");
1490 	if(isnone)
1491 		return reply("550 Permission denied");
1492 	arg = abspath(arg);
1493 	if(arg == 0)
1494 		return reply("550 Error creating %s: Permission denied", arg);
1495 	fd = open(arg, OWRITE);
1496 	if(fd == -1){
1497 		fd = create(arg, OWRITE, createperm);
1498 		if(fd == -1)
1499 			return reply("550 Error creating %s: %r", arg);
1500 	}
1501 	seek(fd, 0, 2);
1502 
1503 	rv = asproc(store, arg, fd);
1504 	close(fd);
1505 	return rv;
1506 }
1507 int
storeucmd(char * arg)1508 storeucmd(char *arg)
1509 {
1510 	int fd, rv;
1511 	char name[Maxpath];
1512 
1513 	USED(arg);
1514 	if(isnone)
1515 		return reply("550 Permission denied");
1516 	strncpy(name, "ftpXXXXXXXXXXX", sizeof name);
1517 	mktemp(name);
1518 	fd = create(name, OWRITE, createperm);
1519 	if(fd == -1)
1520 		return reply("550 Error creating %s: %r", name);
1521 
1522 	rv = asproc(store, name, fd);
1523 	close(fd);
1524 	return rv;
1525 }
1526 
1527 int
mkdircmd(char * name)1528 mkdircmd(char *name)
1529 {
1530 	int fd;
1531 
1532 	if(name == 0)
1533 		return reply("501 Mkdir command requires an argument");
1534 	if(isnone)
1535 		return reply("550 Permission denied");
1536 	name = abspath(name);
1537 	if(name == 0)
1538 		return reply("550 Permission denied");
1539 	fd = create(name, OREAD, DMDIR|0775);
1540 	if(fd < 0)
1541 		return reply("550 Can't create %s: %r", name);
1542 	close(fd);
1543 	return reply("226 %s created", name);
1544 }
1545 
1546 int
delcmd(char * name)1547 delcmd(char *name)
1548 {
1549 	if(name == 0)
1550 		return reply("501 Rmdir/delete command requires an argument");
1551 	if(isnone)
1552 		return reply("550 Permission denied");
1553 	name = abspath(name);
1554 	if(name == 0)
1555 		return reply("550 Permission denied");
1556 	if(remove(name) < 0)
1557 		return reply("550 Can't remove %s: %r", name);
1558 	else
1559 		return reply("226 %s removed", name);
1560 }
1561 
1562 /*
1563  *  kill off the last transfer (if the process still exists)
1564  */
1565 int
abortcmd(char * arg)1566 abortcmd(char *arg)
1567 {
1568 	USED(arg);
1569 
1570 	logit("abort pid %d", pid);
1571 	if(pid){
1572 		if(postnote(PNPROC, pid, "kill") == 0)
1573 			reply("426 Command aborted");
1574 		else
1575 			logit("postnote pid %d %r", pid);
1576 	}
1577 	return reply("226 Abort processed");
1578 }
1579 
1580 int
systemcmd(char * arg)1581 systemcmd(char *arg)
1582 {
1583 	USED(arg);
1584 	return reply("215 UNIX Type: L8 Version: Plan 9");
1585 }
1586 
1587 int
helpcmd(char * arg)1588 helpcmd(char *arg)
1589 {
1590 	int i;
1591 	char buf[80];
1592 	char *p, *e;
1593 
1594 	USED(arg);
1595 	reply("214- the following commands are implemented:");
1596 	p = buf;
1597 	e = buf+sizeof buf;
1598 	for(i = 0; cmdtab[i].name; i++){
1599 		if((i%8) == 0){
1600 			reply("214-%s", buf);
1601 			p = buf;
1602 		}
1603 		p = seprint(p, e, " %-5.5s", cmdtab[i].name);
1604 	}
1605 	if(p != buf)
1606 		reply("214-%s", buf);
1607 	reply("214 ");
1608 	return 0;
1609 }
1610 
1611 /*
1612  *  renaming a file takes two commands
1613  */
1614 static String *filepath;
1615 
1616 int
rnfrcmd(char * from)1617 rnfrcmd(char *from)
1618 {
1619 	if(isnone)
1620 		return reply("550 Permission denied");
1621 	if(from == 0)
1622 		return reply("501 Rename command requires an argument");
1623 	from = abspath(from);
1624 	if(from == 0)
1625 		return reply("550 Permission denied");
1626 	if(filepath == nil)
1627 		filepath = s_copy(from);
1628 	else{
1629 		s_reset(filepath);
1630 		s_append(filepath, from);
1631 	}
1632 	return reply("350 Rename %s to ...", s_to_c(filepath));
1633 }
1634 int
rntocmd(char * to)1635 rntocmd(char *to)
1636 {
1637 	int r;
1638 	Dir nd;
1639 	char *fp, *tp;
1640 
1641 	if(isnone)
1642 		return reply("550 Permission denied");
1643 	if(to == 0)
1644 		return reply("501 Rename command requires an argument");
1645 	to = abspath(to);
1646 	if(to == 0)
1647 		return reply("550 Permission denied");
1648 	if(filepath == nil || *(s_to_c(filepath)) == 0)
1649 		return reply("503 Rnto must be preceeded by an rnfr");
1650 
1651 	tp = strrchr(to, '/');
1652 	fp = strrchr(s_to_c(filepath), '/');
1653 	if((tp && fp == 0) || (fp && tp == 0)
1654 	|| (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to))))
1655 		return reply("550 Rename can't change directory");
1656 	if(tp)
1657 		to = tp+1;
1658 
1659 	nulldir(&nd);
1660 	nd.name = to;
1661 	if(dirwstat(s_to_c(filepath), &nd) < 0)
1662 		r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to);
1663 	else
1664 		r = reply("250 %s now %s", s_to_c(filepath), to);
1665 	s_reset(filepath);
1666 
1667 	return r;
1668 }
1669 
1670 /*
1671  *  to dial out we need the network file system in our
1672  *  name space.
1673  */
1674 int
dialdata(void)1675 dialdata(void)
1676 {
1677 	int fd, cfd;
1678 	char ldir[40];
1679 	char err[Maxerr];
1680 
1681 	if(mountnet() < 0)
1682 		return -1;
1683 
1684 	if(!passive.inuse){
1685 		fd = dial(data, "20", 0, 0);
1686 		errstr(err, sizeof err);
1687 	} else {
1688 		alarm(5*60*1000);
1689 		cfd = listen(passive.adir, ldir);
1690 		alarm(0);
1691 		errstr(err, sizeof err);
1692 		if(cfd < 0)
1693 			return -1;
1694 		fd = accept(cfd, ldir);
1695 		errstr(err, sizeof err);
1696 		close(cfd);
1697 	}
1698 	if(fd < 0)
1699 		logit("can't dial %s: %s", data, err);
1700 
1701 	unmountnet();
1702 	werrstr(err, sizeof err);
1703 	return fd;
1704 }
1705 
1706 int
postnote(int group,int pid,char * note)1707 postnote(int group, int pid, char *note)
1708 {
1709 	char file[128];
1710 	int f, r;
1711 
1712 	/*
1713 	 * Use #p because /proc may not be in the namespace.
1714 	 */
1715 	switch(group) {
1716 	case PNPROC:
1717 		sprint(file, "#p/%d/note", pid);
1718 		break;
1719 	case PNGROUP:
1720 		sprint(file, "#p/%d/notepg", pid);
1721 		break;
1722 	default:
1723 		return -1;
1724 	}
1725 
1726 	f = open(file, OWRITE);
1727 	if(f < 0)
1728 		return -1;
1729 
1730 	r = strlen(note);
1731 	if(write(f, note, r) != r) {
1732 		close(f);
1733 		return -1;
1734 	}
1735 	close(f);
1736 	return 0;
1737 }
1738 
1739 /*
1740  *  to circumscribe the accessible files we have to eliminate ..'s
1741  *  and resolve all names from the root.  We also remove any /bin/rc
1742  *  special characters to avoid later problems with executed commands.
1743  */
1744 char *special = "`;| ";
1745 
1746 char*
abspath(char * origpath)1747 abspath(char *origpath)
1748 {
1749 	char *p, *sp, *path;
1750 	static String *rpath;
1751 
1752 	if(rpath == nil)
1753 		rpath = s_new();
1754 	else
1755 		s_reset(rpath);
1756 
1757 	if(origpath == nil)
1758 		s_append(rpath, curdir);
1759 	else{
1760 		if(*origpath != '/'){
1761 			s_append(rpath, curdir);
1762 			s_append(rpath, "/");
1763 		}
1764 		s_append(rpath, origpath);
1765 	}
1766 	path = s_to_c(rpath);
1767 
1768 	for(sp = special; *sp; sp++){
1769 		p = strchr(path, *sp);
1770 		if(p)
1771 			*p = 0;
1772 	}
1773 
1774 	cleanname(s_to_c(rpath));
1775 	rpath->ptr = rpath->base+strlen(rpath->base);
1776 
1777 	if(!accessok(s_to_c(rpath)))
1778 		return nil;
1779 
1780 	return s_to_c(rpath);
1781 }
1782 
1783 typedef struct Path Path;
1784 struct Path {
1785 	Path	*next;
1786 	String	*path;
1787 	int	inuse;
1788 	int	ok;
1789 };
1790 
1791 enum
1792 {
1793 	Maxlevel = 16,
1794 	Maxperlevel= 8,
1795 };
1796 
1797 Path *pathlevel[Maxlevel];
1798 
1799 Path*
unlinkpath(char * path,int level)1800 unlinkpath(char *path, int level)
1801 {
1802 	String *s;
1803 	Path **l, *p;
1804 	int n;
1805 
1806 	n = 0;
1807 	for(l = &pathlevel[level]; *l; l = &(*l)->next){
1808 		p = *l;
1809 		/* hit */
1810 		if(strcmp(s_to_c(p->path), path) == 0){
1811 			*l = p->next;
1812 			p->next = nil;
1813 			return p;
1814 		}
1815 		/* reuse */
1816 		if(++n >= Maxperlevel){
1817 			*l = p->next;
1818 			s = p->path;
1819 			s_reset(p->path);
1820 			memset(p, 0, sizeof *p);
1821 			p->path = s_append(s, path);
1822 			return p;
1823 		}
1824 	}
1825 
1826 	/* allocate */
1827 	p = mallocz(sizeof *p, 1);
1828 	p->path = s_copy(path);
1829 	return p;
1830 }
1831 
1832 void
linkpath(Path * p,int level)1833 linkpath(Path *p, int level)
1834 {
1835 	p->next = pathlevel[level];
1836 	pathlevel[level] = p;
1837 	p->inuse = 1;
1838 }
1839 
1840 void
addpath(Path * p,int level,int ok)1841 addpath(Path *p, int level, int ok)
1842 {
1843 	p->ok = ok;
1844 	p->next = pathlevel[level];
1845 	pathlevel[level] = p;
1846 }
1847 
1848 int
_accessok(String * s,int level)1849 _accessok(String *s, int level)
1850 {
1851 	Path *p;
1852 	char *cp;
1853 	int lvl, offset;
1854 	static char httplogin[] = "/.httplogin";
1855 
1856 	if(level < 0)
1857 		return 1;
1858 	lvl = level;
1859 	if(lvl >= Maxlevel)
1860 		lvl = Maxlevel - 1;
1861 
1862 	p = unlinkpath(s_to_c(s), lvl);
1863 	if(p->inuse){
1864 		/* move to front */
1865 		linkpath(p, lvl);
1866 		return p->ok;
1867 	}
1868 	cp = strrchr(s_to_c(s), '/');
1869 	if(cp == nil)
1870 		offset = 0;
1871 	else
1872 		offset = cp - s_to_c(s);
1873 	s_append(s, httplogin);
1874 	if(access(s_to_c(s), AEXIST) == 0){
1875 		addpath(p, lvl, 0);
1876 		return 0;
1877 	}
1878 
1879 	/*
1880 	 * There's no way to shorten a String without
1881 	 * knowing the implementation.
1882 	 */
1883 	s->ptr = s->base+offset;
1884 	s_terminate(s);
1885 	addpath(p, lvl, _accessok(s, level-1));
1886 
1887 	return p->ok;
1888 }
1889 
1890 /*
1891  * check for a subdirectory containing .httplogin
1892  * at each level of the path.
1893  */
1894 int
accessok(char * path)1895 accessok(char *path)
1896 {
1897 	int level, r;
1898 	char *p;
1899 	String *npath;
1900 
1901 	npath = s_copy(path);
1902 	p = s_to_c(npath)+1;
1903 	for(level = 1; level < Maxlevel; level++){
1904 		p = strchr(p, '/');
1905 		if(p == nil)
1906 			break;
1907 		p++;
1908 	}
1909 
1910 	r = _accessok(npath, level-1);
1911 	s_free(npath);
1912 
1913 	return r;
1914 }
1915