Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
682
cdupcmd(char *dp)
683
{
684
	USED(dp);
685
	return cwdcmd("..");
686
}
687
 
688
int
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
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
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
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
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
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
803
unmountnet(void)
804
{
805
	unmount(0, "/net");
806
	unmount("#/", "/");
807
}
808
 
809
int
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*
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
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
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
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
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
1116
namelistcmd(char *arg)
1117
{
1118
	return asproc(list, arg, 0);
1119
}
1120
int
1121
listcmd(char *arg)
1122
{
1123
	return asproc(list, arg, 1);
1124
}
1125
 
1126
/*
1127
 * fuse compatability
1128
 */
1129
int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1581
systemcmd(char *arg)
1582
{
1583
	USED(arg);
1584
	return reply("215 UNIX Type: L8 Version: Plan 9");
1585
}
1586
 
1587
int
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
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
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
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
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*
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*
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
1833
linkpath(Path *p, int level)
1834
{
1835
	p->next = pathlevel[level];
1836
	pathlevel[level] = p;
1837
	p->inuse = 1;
1838
}
1839
 
1840
void
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
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
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
}