xref: /plan9/sys/src/cmd/ip/ftpfs/proto.c (revision 1269a55e17378f8f0a601a13c99e4e804209c23f)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ip.h>
5*1269a55eSDavid du Colombier #include <mp.h>
6*1269a55eSDavid du Colombier #include <libsec.h>
79a747e4fSDavid du Colombier #include <auth.h>
89a747e4fSDavid du Colombier #include <fcall.h>
99a747e4fSDavid du Colombier #include <ctype.h>
109a747e4fSDavid du Colombier #include <String.h>
119a747e4fSDavid du Colombier #include "ftpfs.h"
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier enum
149a747e4fSDavid du Colombier {
159a747e4fSDavid du Colombier 	/* return codes */
169a747e4fSDavid du Colombier 	Extra=		1,
179a747e4fSDavid du Colombier 	Success=	2,
189a747e4fSDavid du Colombier 	Incomplete=	3,
199a747e4fSDavid du Colombier 	TempFail=	4,
209a747e4fSDavid du Colombier 	PermFail=	5,
219a747e4fSDavid du Colombier 	Impossible=	6,
229a747e4fSDavid du Colombier };
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier Node	*remdir;		/* current directory on remote machine */
259a747e4fSDavid du Colombier Node	*remroot;		/* root directory on remote machine */
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier int	ctlfd;			/* fd for control connection */
289a747e4fSDavid du Colombier Biobuf	ctlin;			/* input buffer for control connection */
299a747e4fSDavid du Colombier Biobuf	stdin;			/* input buffer for standard input */
309a747e4fSDavid du Colombier Biobuf	dbuf;			/* buffer for data connection */
319a747e4fSDavid du Colombier char	msg[512];		/* buffer for replies */
329a747e4fSDavid du Colombier char	net[Maxpath];		/* network for connections */
339a747e4fSDavid du Colombier int	listenfd;		/* fd to listen on for connections */
349a747e4fSDavid du Colombier char	netdir[Maxpath];
359a747e4fSDavid du Colombier int	os, defos;
369a747e4fSDavid du Colombier char	topsdir[64];		/* name of listed directory for TOPS */
379a747e4fSDavid du Colombier String	*remrootpath;	/* path on remote side to remote root */
389a747e4fSDavid du Colombier char	*user;
399a747e4fSDavid du Colombier int	nopassive;
409a747e4fSDavid du Colombier long	lastsend;
41*1269a55eSDavid du Colombier extern int usetls;
429a747e4fSDavid du Colombier 
439a747e4fSDavid du Colombier static void	sendrequest(char*, char*);
449a747e4fSDavid du Colombier static int	getreply(Biobuf*, char*, int, int);
459a747e4fSDavid du Colombier static int	active(int, Biobuf**, char*, char*);
469a747e4fSDavid du Colombier static int	passive(int, Biobuf**, char*, char*);
479a747e4fSDavid du Colombier static int	data(int, Biobuf**, char*, char*);
489a747e4fSDavid du Colombier static int	port(void);
499a747e4fSDavid du Colombier static void	ascii(void);
509a747e4fSDavid du Colombier static void	image(void);
519a747e4fSDavid du Colombier static void	unixpath(Node*, String*);
529a747e4fSDavid du Colombier static void	vmspath(Node*, String*);
539a747e4fSDavid du Colombier static void	mvspath(Node*, String*);
549a747e4fSDavid du Colombier static Node*	vmsdir(char*);
559a747e4fSDavid du Colombier static int	getpassword(char*, char*);
56ecc2a59cSDavid du Colombier static int	nw_mode(char dirlet, char *s);
579a747e4fSDavid du Colombier 
589a747e4fSDavid du Colombier /*
599a747e4fSDavid du Colombier  *  connect to remote server, default network is "tcp/ip"
609a747e4fSDavid du Colombier  */
619a747e4fSDavid du Colombier void
629a747e4fSDavid du Colombier hello(char *dest)
639a747e4fSDavid du Colombier {
649a747e4fSDavid du Colombier 	char *p;
659a747e4fSDavid du Colombier 	char dir[Maxpath];
66*1269a55eSDavid du Colombier 	int ts;
67*1269a55eSDavid du Colombier 	TLSconn conn;
689a747e4fSDavid du Colombier 
699a747e4fSDavid du Colombier 	Binit(&stdin, 0, OREAD);	/* init for later use */
709a747e4fSDavid du Colombier 
719a747e4fSDavid du Colombier 	ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
729a747e4fSDavid du Colombier 	if(ctlfd < 0){
739a747e4fSDavid du Colombier 		fprint(2, "can't dial %s: %r\n", dest);
749a747e4fSDavid du Colombier 		exits("dialing");
759a747e4fSDavid du Colombier 	}
76*1269a55eSDavid du Colombier 
779a747e4fSDavid du Colombier 	Binit(&ctlin, ctlfd, OREAD);
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier 	/* remember network for the data connections */
809a747e4fSDavid du Colombier 	p = strrchr(dir+1, '/');
819a747e4fSDavid du Colombier 	if(p == 0)
829a747e4fSDavid du Colombier 		fatal("wrong dial(2) linked with ftp");
839a747e4fSDavid du Colombier 	*p = 0;
849a747e4fSDavid du Colombier 	safecpy(net, dir, sizeof(net));
859a747e4fSDavid du Colombier 
869a747e4fSDavid du Colombier 	/* wait for hello from other side */
879a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
889a747e4fSDavid du Colombier 		fatal("bad hello");
89d9306527SDavid du Colombier 	if(strstr(msg, "Plan 9"))
90d9306527SDavid du Colombier 		os = Plan9;
91*1269a55eSDavid du Colombier 
92*1269a55eSDavid du Colombier 	if(usetls){
93*1269a55eSDavid du Colombier 		sendrequest("AUTH", "TLS");
94*1269a55eSDavid du Colombier 		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
95*1269a55eSDavid du Colombier 			fatal("bad auth tls");
96*1269a55eSDavid du Colombier 
97*1269a55eSDavid du Colombier 		ctlfd = tlsClient(ctlfd, &conn);
98*1269a55eSDavid du Colombier 		if(ctlfd < 0)
99*1269a55eSDavid du Colombier 			fatal("starting tls: %r");
100*1269a55eSDavid du Colombier 		free(conn.cert);
101*1269a55eSDavid du Colombier 
102*1269a55eSDavid du Colombier 		Binit(&ctlin, ctlfd, OREAD);
103*1269a55eSDavid du Colombier 
104*1269a55eSDavid du Colombier 		sendrequest("PBSZ", "0");
105*1269a55eSDavid du Colombier 		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
106*1269a55eSDavid du Colombier 			fatal("bad pbsz 0");
107*1269a55eSDavid du Colombier 		sendrequest("PROT", "P");
108*1269a55eSDavid du Colombier 		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
109*1269a55eSDavid du Colombier 			fatal("bad prot p");
110*1269a55eSDavid du Colombier 	}
1119a747e4fSDavid du Colombier }
1129a747e4fSDavid du Colombier 
1139a747e4fSDavid du Colombier /*
1149a747e4fSDavid du Colombier  *  login to remote system
1159a747e4fSDavid du Colombier  */
1169a747e4fSDavid du Colombier void
117eaa278a2SDavid du Colombier rlogin(char *rsys, char *keyspec)
1189a747e4fSDavid du Colombier {
1199a747e4fSDavid du Colombier 	char *line;
1209a747e4fSDavid du Colombier 	char pass[128];
121d9306527SDavid du Colombier 	UserPasswd *up;
1229a747e4fSDavid du Colombier 
123d9306527SDavid du Colombier 	up = nil;
1249a747e4fSDavid du Colombier 	for(;;){
125d9306527SDavid du Colombier 		if(up == nil && os != Plan9)
126eaa278a2SDavid du Colombier 			up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
127d9306527SDavid du Colombier 		if(up != nil){
128d9306527SDavid du Colombier 			sendrequest("USER", up->user);
129d9306527SDavid du Colombier 		} else {
1309a747e4fSDavid du Colombier 			print("User[default = %s]: ", user);
1319a747e4fSDavid du Colombier 			line = Brdline(&stdin, '\n');
1329a747e4fSDavid du Colombier 			if(line == 0)
1339a747e4fSDavid du Colombier 				exits(0);
1349a747e4fSDavid du Colombier 			line[Blinelen(&stdin)-1] = 0;
1359a747e4fSDavid du Colombier 			if(*line){
1369a747e4fSDavid du Colombier 				free(user);
1379a747e4fSDavid du Colombier 				user = strdup(line);
1389a747e4fSDavid du Colombier 			}
1399a747e4fSDavid du Colombier 			sendrequest("USER", user);
140d9306527SDavid du Colombier 		}
1419a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 1)){
1429a747e4fSDavid du Colombier 		case Success:
143d9306527SDavid du Colombier 			goto out;
1449a747e4fSDavid du Colombier 		case Incomplete:
1459a747e4fSDavid du Colombier 			break;
1469a747e4fSDavid du Colombier 		case TempFail:
1479a747e4fSDavid du Colombier 		case PermFail:
1489a747e4fSDavid du Colombier 			continue;
1499a747e4fSDavid du Colombier 		}
1509a747e4fSDavid du Colombier 
151d9306527SDavid du Colombier 		if(up != nil){
152d9306527SDavid du Colombier 			sendrequest("PASS", up->passwd);
153d9306527SDavid du Colombier 		} else {
1549a747e4fSDavid du Colombier 			if(getpassword(pass, pass+sizeof(pass)) < 0)
1559a747e4fSDavid du Colombier 				exits(0);
1569a747e4fSDavid du Colombier 			sendrequest("PASS", pass);
157d9306527SDavid du Colombier 		}
1589a747e4fSDavid du Colombier 		if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
1599a747e4fSDavid du Colombier 			if(strstr(msg, "Sess#"))
1609a747e4fSDavid du Colombier 				defos = MVS;
161d9306527SDavid du Colombier 			break;
1629a747e4fSDavid du Colombier 		}
1639a747e4fSDavid du Colombier 	}
164d9306527SDavid du Colombier out:
165d9306527SDavid du Colombier 	if(up != nil){
166d9306527SDavid du Colombier 		memset(up, 0, sizeof(*up));
167d9306527SDavid du Colombier 		free(up);
168d9306527SDavid du Colombier 	}
1699a747e4fSDavid du Colombier }
1709a747e4fSDavid du Colombier 
1719a747e4fSDavid du Colombier /*
1729a747e4fSDavid du Colombier  *  login to remote system with given user name and password.
1739a747e4fSDavid du Colombier  */
1749a747e4fSDavid du Colombier void
1759a747e4fSDavid du Colombier clogin(char *cuser, char *cpassword)
1769a747e4fSDavid du Colombier {
1779a747e4fSDavid du Colombier 	free(user);
1789a747e4fSDavid du Colombier 	user = strdup(cuser);
1799a747e4fSDavid du Colombier 	if (strcmp(user, "anonymous") != 0 &&
1809a747e4fSDavid du Colombier 	    strcmp(user, "ftp") != 0)
1819a747e4fSDavid du Colombier 		fatal("User must be 'anonymous' or 'ftp'");
1829a747e4fSDavid du Colombier 	sendrequest("USER", user);
1839a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
1849a747e4fSDavid du Colombier 	case Success:
1859a747e4fSDavid du Colombier 		return;
1869a747e4fSDavid du Colombier 	case Incomplete:
1879a747e4fSDavid du Colombier 		break;
1889a747e4fSDavid du Colombier 	case TempFail:
1899a747e4fSDavid du Colombier 	case PermFail:
1909a747e4fSDavid du Colombier 		fatal("login failed");
1919a747e4fSDavid du Colombier 	}
1929a747e4fSDavid du Colombier 	if (cpassword == 0)
1939a747e4fSDavid du Colombier 		fatal("password needed");
1949a747e4fSDavid du Colombier 	sendrequest("PASS", cpassword);
1959a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
1969a747e4fSDavid du Colombier 		fatal("password failed");
1979a747e4fSDavid du Colombier 	if(strstr(msg, "Sess#"))
1989a747e4fSDavid du Colombier 		defos = MVS;
1999a747e4fSDavid du Colombier 	return;
2009a747e4fSDavid du Colombier }
2019a747e4fSDavid du Colombier 
2029a747e4fSDavid du Colombier /*
2039a747e4fSDavid du Colombier  *  find out about the other side.  go to it's root if requested.  set
2049a747e4fSDavid du Colombier  *  image mode if a Plan9 system.
2059a747e4fSDavid du Colombier  */
2069a747e4fSDavid du Colombier void
2079a747e4fSDavid du Colombier preamble(char *mountroot)
2089a747e4fSDavid du Colombier {
2099a747e4fSDavid du Colombier 	char *p, *ep;
2109a747e4fSDavid du Colombier 	int rv;
2119a747e4fSDavid du Colombier 	OS *o;
2129a747e4fSDavid du Colombier 
2139a747e4fSDavid du Colombier 	/*
2149a747e4fSDavid du Colombier 	 *  create a root directory mirror
2159a747e4fSDavid du Colombier 	 */
2169a747e4fSDavid du Colombier 	remroot = newnode(0, s_copy("/"));
2179a747e4fSDavid du Colombier 	remroot->d->qid.type = QTDIR;
2189a747e4fSDavid du Colombier 	remroot->d->mode = DMDIR|0777;
2199a747e4fSDavid du Colombier 	remdir = remroot;
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier 	/*
2229a747e4fSDavid du Colombier 	 *  get system type
2239a747e4fSDavid du Colombier 	 */
2249a747e4fSDavid du Colombier 	sendrequest("SYST", nil);
2259a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
2269a747e4fSDavid du Colombier 	case Success:
2279a747e4fSDavid du Colombier 		for(o = oslist; o->os != Unknown; o++)
2289a747e4fSDavid du Colombier 			if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
2299a747e4fSDavid du Colombier 				break;
2309a747e4fSDavid du Colombier 		os = o->os;
2319a747e4fSDavid du Colombier 		if(os == NT)
2329a747e4fSDavid du Colombier 			os = Unix;
2339a747e4fSDavid du Colombier 		break;
2349a747e4fSDavid du Colombier 	default:
2359a747e4fSDavid du Colombier 		os = defos;
2369a747e4fSDavid du Colombier 		break;
2379a747e4fSDavid du Colombier 	}
2389a747e4fSDavid du Colombier 	if(os == Unknown)
2399a747e4fSDavid du Colombier 		os = defos;
2409a747e4fSDavid du Colombier 
2419a747e4fSDavid du Colombier 	remrootpath = s_reset(remrootpath);
2429a747e4fSDavid du Colombier 	switch(os){
2436b6b9ac8SDavid du Colombier 	case NetWare:
2446b6b9ac8SDavid du Colombier               /*
2456b6b9ac8SDavid du Colombier                * Request long, rather than 8.3 filenames,
2466b6b9ac8SDavid du Colombier                * where the Servers & Volume support them.
2476b6b9ac8SDavid du Colombier                */
2486b6b9ac8SDavid du Colombier               sendrequest("SITE LONG", nil);
2496b6b9ac8SDavid du Colombier               getreply(&ctlin, msg, sizeof(msg), 0);
2506b6b9ac8SDavid du Colombier               /* FALL THRU */
2519a747e4fSDavid du Colombier 	case Unix:
2529a747e4fSDavid du Colombier 	case Plan9:
2539a747e4fSDavid du Colombier 		/*
2549a747e4fSDavid du Colombier 		 *  go to the remote root, if asked
2559a747e4fSDavid du Colombier 		 */
2569a747e4fSDavid du Colombier 		if(mountroot){
2579a747e4fSDavid du Colombier 			sendrequest("CWD", mountroot);
2589a747e4fSDavid du Colombier 			getreply(&ctlin, msg, sizeof(msg), 0);
2599a747e4fSDavid du Colombier 		} else {
2609a747e4fSDavid du Colombier 			s_append(remrootpath, "/usr/");
2619a747e4fSDavid du Colombier 			s_append(remrootpath, user);
2629a747e4fSDavid du Colombier 		}
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier 		/*
2659a747e4fSDavid du Colombier 		 *  get the root directory
2669a747e4fSDavid du Colombier 		 */
2679a747e4fSDavid du Colombier 		sendrequest("PWD", nil);
2689a747e4fSDavid du Colombier 		rv = getreply(&ctlin, msg, sizeof(msg), 1);
2699a747e4fSDavid du Colombier 		if(rv == PermFail){
2709a747e4fSDavid du Colombier 			sendrequest("XPWD", nil);
2719a747e4fSDavid du Colombier 			rv = getreply(&ctlin, msg, sizeof(msg), 1);
2729a747e4fSDavid du Colombier 		}
2739a747e4fSDavid du Colombier 		if(rv == Success){
2749a747e4fSDavid du Colombier 			p = strchr(msg, '"');
2759a747e4fSDavid du Colombier 			if(p){
2769a747e4fSDavid du Colombier 				p++;
2779a747e4fSDavid du Colombier 				ep = strchr(p, '"');
2789a747e4fSDavid du Colombier 				if(ep){
2799a747e4fSDavid du Colombier 					*ep = 0;
2809a747e4fSDavid du Colombier 					s_append(s_reset(remrootpath), p);
2819a747e4fSDavid du Colombier 				}
2829a747e4fSDavid du Colombier 			}
2839a747e4fSDavid du Colombier 		}
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier 		break;
2869a747e4fSDavid du Colombier 	case Tops:
2879a747e4fSDavid du Colombier 	case VM:
2889a747e4fSDavid du Colombier 		/*
2899a747e4fSDavid du Colombier 		 *  top directory is a figment of our imagination.
2909a747e4fSDavid du Colombier 		 *  make it permanently cached & valid.
2919a747e4fSDavid du Colombier 		 */
2929a747e4fSDavid du Colombier 		CACHED(remroot);
2939a747e4fSDavid du Colombier 		VALID(remroot);
2949a747e4fSDavid du Colombier 		remroot->d->atime = time(0) + 100000;
2959a747e4fSDavid du Colombier 
2969a747e4fSDavid du Colombier 		/*
2979a747e4fSDavid du Colombier 		 *  no initial directory.  We are in the
2989a747e4fSDavid du Colombier 		 *  imaginary root.
2999a747e4fSDavid du Colombier 		 */
3009a747e4fSDavid du Colombier 		remdir = newtopsdir("???");
3019a747e4fSDavid du Colombier 		topsdir[0] = 0;
3029a747e4fSDavid du Colombier 		if(os == Tops && readdir(remdir) >= 0){
3039a747e4fSDavid du Colombier 			CACHED(remdir);
3049a747e4fSDavid du Colombier 			if(*topsdir)
3059a747e4fSDavid du Colombier 				remdir->remname = s_copy(topsdir);
3069a747e4fSDavid du Colombier 			VALID(remdir);
3079a747e4fSDavid du Colombier 		}
3089a747e4fSDavid du Colombier 		break;
3099a747e4fSDavid du Colombier 	case VMS:
3109a747e4fSDavid du Colombier 		/*
3119a747e4fSDavid du Colombier 		 *  top directory is a figment of our imagination.
3129a747e4fSDavid du Colombier 		 *  make it permanently cached & valid.
3139a747e4fSDavid du Colombier 		 */
3149a747e4fSDavid du Colombier 		CACHED(remroot);
3159a747e4fSDavid du Colombier 		VALID(remroot);
3169a747e4fSDavid du Colombier 		remroot->d->atime = time(0) + 100000;
3179a747e4fSDavid du Colombier 
3189a747e4fSDavid du Colombier 		/*
3199a747e4fSDavid du Colombier 		 *  get current directory
3209a747e4fSDavid du Colombier 		 */
3219a747e4fSDavid du Colombier 		sendrequest("PWD", nil);
3229a747e4fSDavid du Colombier 		rv = getreply(&ctlin, msg, sizeof(msg), 1);
3239a747e4fSDavid du Colombier 		if(rv == PermFail){
3249a747e4fSDavid du Colombier 			sendrequest("XPWD", nil);
3259a747e4fSDavid du Colombier 			rv = getreply(&ctlin, msg, sizeof(msg), 1);
3269a747e4fSDavid du Colombier 		}
3279a747e4fSDavid du Colombier 		if(rv == Success){
3289a747e4fSDavid du Colombier 			p = strchr(msg, '"');
3299a747e4fSDavid du Colombier 			if(p){
3309a747e4fSDavid du Colombier 				p++;
3319a747e4fSDavid du Colombier 				ep = strchr(p, '"');
3329a747e4fSDavid du Colombier 				if(ep){
3339a747e4fSDavid du Colombier 					*ep = 0;
3349a747e4fSDavid du Colombier 					remroot = remdir = vmsdir(p);
3359a747e4fSDavid du Colombier 				}
3369a747e4fSDavid du Colombier 			}
3379a747e4fSDavid du Colombier 		}
3389a747e4fSDavid du Colombier 		break;
3399a747e4fSDavid du Colombier 	case MVS:
3409a747e4fSDavid du Colombier 		usenlst = 1;
3419a747e4fSDavid du Colombier 		break;
3429a747e4fSDavid du Colombier 	}
3439a747e4fSDavid du Colombier 
3449a747e4fSDavid du Colombier 	if(os == Plan9)
3459a747e4fSDavid du Colombier 		image();
3469a747e4fSDavid du Colombier }
3479a747e4fSDavid du Colombier 
3489a747e4fSDavid du Colombier static void
3499a747e4fSDavid du Colombier ascii(void)
3509a747e4fSDavid du Colombier {
3519a747e4fSDavid du Colombier 	sendrequest("TYPE A", nil);
3529a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
3539a747e4fSDavid du Colombier 	case Success:
3549a747e4fSDavid du Colombier 		break;
3559a747e4fSDavid du Colombier 	default:
3569a747e4fSDavid du Colombier 		fatal("can't set type to ascii");
3579a747e4fSDavid du Colombier 	}
3589a747e4fSDavid du Colombier }
3599a747e4fSDavid du Colombier 
3609a747e4fSDavid du Colombier static void
3619a747e4fSDavid du Colombier image(void)
3629a747e4fSDavid du Colombier {
3639a747e4fSDavid du Colombier 	sendrequest("TYPE I", nil);
3649a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
3659a747e4fSDavid du Colombier 	case Success:
3669a747e4fSDavid du Colombier 		break;
3679a747e4fSDavid du Colombier 	default:
3689a747e4fSDavid du Colombier 		fatal("can't set type to image/binary");
3699a747e4fSDavid du Colombier 	}
3709a747e4fSDavid du Colombier }
3719a747e4fSDavid du Colombier 
3729a747e4fSDavid du Colombier /*
3739a747e4fSDavid du Colombier  *  decode the time fields, return seconds since epoch began
3749a747e4fSDavid du Colombier  */
3759a747e4fSDavid du Colombier char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
3769a747e4fSDavid du Colombier static Tm now;
3779a747e4fSDavid du Colombier 
3789a747e4fSDavid du Colombier static ulong
3799a747e4fSDavid du Colombier cracktime(char *month, char *day, char *yr, char *hms)
3809a747e4fSDavid du Colombier {
3819a747e4fSDavid du Colombier 	Tm tm;
3829a747e4fSDavid du Colombier 	int i;
3839a747e4fSDavid du Colombier 	char *p;
3849a747e4fSDavid du Colombier 
3856b6b9ac8SDavid du Colombier 
3869a747e4fSDavid du Colombier 	/* default time */
3879a747e4fSDavid du Colombier 	if(now.year == 0)
3889a747e4fSDavid du Colombier 		now = *localtime(time(0));
3899a747e4fSDavid du Colombier 	tm = now;
3906b6b9ac8SDavid du Colombier 	tm.yday = 0;
3919a747e4fSDavid du Colombier 
3929a747e4fSDavid du Colombier 	/* convert ascii month to a number twixt 1 and 12 */
3939a747e4fSDavid du Colombier 	if(*month >= '0' && *month <= '9'){
3949a747e4fSDavid du Colombier 		tm.mon = atoi(month) - 1;
3959a747e4fSDavid du Colombier 		if(tm.mon < 0 || tm.mon > 11)
3969a747e4fSDavid du Colombier 			tm.mon = 5;
3979a747e4fSDavid du Colombier 	} else {
3989a747e4fSDavid du Colombier 		for(p = month; *p; p++)
3999a747e4fSDavid du Colombier 			*p = tolower(*p);
4009a747e4fSDavid du Colombier 		for(i = 0; i < 12; i++)
4019a747e4fSDavid du Colombier 			if(strncmp(&monthchars[i*3], month, 3) == 0){
4029a747e4fSDavid du Colombier 				tm.mon = i;
4039a747e4fSDavid du Colombier 				break;
4049a747e4fSDavid du Colombier 			}
4059a747e4fSDavid du Colombier 	}
4069a747e4fSDavid du Colombier 
4079a747e4fSDavid du Colombier 	tm.mday = atoi(day);
4089a747e4fSDavid du Colombier 
4099a747e4fSDavid du Colombier 	if(hms){
4109a747e4fSDavid du Colombier 		tm.hour = strtol(hms, &p, 0);
4119a747e4fSDavid du Colombier 		if(*p == ':'){
4129a747e4fSDavid du Colombier 			tm.min = strtol(p+1, &p, 0);
4139a747e4fSDavid du Colombier 			if(*p == ':')
4149a747e4fSDavid du Colombier 				tm.sec = strtol(p+1, &p, 0);
4159a747e4fSDavid du Colombier 		}
4169a747e4fSDavid du Colombier 		if(tolower(*p) == 'p')
4179a747e4fSDavid du Colombier 			tm.hour += 12;
4189a747e4fSDavid du Colombier 	}
4199a747e4fSDavid du Colombier 
4209a747e4fSDavid du Colombier 	if(yr){
4219a747e4fSDavid du Colombier 		tm.year = atoi(yr);
4229a747e4fSDavid du Colombier 		if(tm.year >= 1900)
4239a747e4fSDavid du Colombier 			tm.year -= 1900;
4249a747e4fSDavid du Colombier 	} else {
4259a747e4fSDavid du Colombier 		if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
4269a747e4fSDavid du Colombier 			tm.year--;
4279a747e4fSDavid du Colombier 	}
4289a747e4fSDavid du Colombier 
4299a747e4fSDavid du Colombier 	/* convert to epoch seconds */
4309a747e4fSDavid du Colombier 	return tm2sec(&tm);
4319a747e4fSDavid du Colombier }
4329a747e4fSDavid du Colombier 
4339a747e4fSDavid du Colombier /*
4349a747e4fSDavid du Colombier  *  decode a Unix or Plan 9 file mode
4359a747e4fSDavid du Colombier  */
4369a747e4fSDavid du Colombier static ulong
4379a747e4fSDavid du Colombier crackmode(char *p)
4389a747e4fSDavid du Colombier {
4399a747e4fSDavid du Colombier 	ulong flags;
4409a747e4fSDavid du Colombier 	ulong mode;
4419a747e4fSDavid du Colombier 	int i;
4429a747e4fSDavid du Colombier 
4439a747e4fSDavid du Colombier 	flags = 0;
4449a747e4fSDavid du Colombier 	switch(strlen(p)){
4459a747e4fSDavid du Colombier 	case 10:	/* unix and new style plan 9 */
4469a747e4fSDavid du Colombier 		switch(*p){
4479a747e4fSDavid du Colombier 		case 'l':
4489a747e4fSDavid du Colombier 			return DMSYML|0777;
4499a747e4fSDavid du Colombier 		case 'd':
4509a747e4fSDavid du Colombier 			flags |= DMDIR;
4519a747e4fSDavid du Colombier 		case 'a':
4529a747e4fSDavid du Colombier 			flags |= DMAPPEND;
4539a747e4fSDavid du Colombier 		}
4549a747e4fSDavid du Colombier 		p++;
4559a747e4fSDavid du Colombier 		if(p[2] == 'l')
4569a747e4fSDavid du Colombier 			flags |= DMEXCL;
4579a747e4fSDavid du Colombier 		break;
4589a747e4fSDavid du Colombier 	case 11:	/* old style plan 9 */
4599a747e4fSDavid du Colombier 		switch(*p++){
4609a747e4fSDavid du Colombier 		case 'd':
4619a747e4fSDavid du Colombier 			flags |= DMDIR;
4629a747e4fSDavid du Colombier 			break;
4639a747e4fSDavid du Colombier 		case 'a':
4649a747e4fSDavid du Colombier 			flags |= DMAPPEND;
4659a747e4fSDavid du Colombier 			break;
4669a747e4fSDavid du Colombier 		}
4679a747e4fSDavid du Colombier 		if(*p++ == 'l')
4689a747e4fSDavid du Colombier 			flags |= DMEXCL;
4699a747e4fSDavid du Colombier 		break;
4709a747e4fSDavid du Colombier 	default:
4719a747e4fSDavid du Colombier 		return DMDIR|0777;
4729a747e4fSDavid du Colombier 	}
4739a747e4fSDavid du Colombier 	mode = 0;
4749a747e4fSDavid du Colombier 	for(i = 0; i < 3; i++){
4759a747e4fSDavid du Colombier 		mode <<= 3;
4769a747e4fSDavid du Colombier 		if(*p++ == 'r')
4779a747e4fSDavid du Colombier 			mode |= DMREAD;
4789a747e4fSDavid du Colombier 		if(*p++ == 'w')
4799a747e4fSDavid du Colombier 			mode |= DMWRITE;
4809a747e4fSDavid du Colombier 		if(*p == 'x' || *p == 's' || *p == 'S')
4819a747e4fSDavid du Colombier 			mode |= DMEXEC;
4829a747e4fSDavid du Colombier 		p++;
4839a747e4fSDavid du Colombier 	}
4849a747e4fSDavid du Colombier 	return mode | flags;
4859a747e4fSDavid du Colombier }
4869a747e4fSDavid du Colombier 
4879a747e4fSDavid du Colombier /*
4889a747e4fSDavid du Colombier  *  find first punctuation
4899a747e4fSDavid du Colombier  */
4909a747e4fSDavid du Colombier char*
4919a747e4fSDavid du Colombier strpunct(char *p)
4929a747e4fSDavid du Colombier {
4939a747e4fSDavid du Colombier 	int c;
4949a747e4fSDavid du Colombier 
4959a747e4fSDavid du Colombier 	for(;c = *p; p++){
4969a747e4fSDavid du Colombier 		if(ispunct(c))
4979a747e4fSDavid du Colombier 			return p;
4989a747e4fSDavid du Colombier 	}
4999a747e4fSDavid du Colombier 	return 0;
5009a747e4fSDavid du Colombier }
5019a747e4fSDavid du Colombier 
5029a747e4fSDavid du Colombier /*
5039a747e4fSDavid du Colombier  *  decode a Unix or Plan 9 directory listing
5049a747e4fSDavid du Colombier  */
5059a747e4fSDavid du Colombier static Dir*
5069a747e4fSDavid du Colombier crackdir(char *p, String **remname)
5079a747e4fSDavid du Colombier {
5089a747e4fSDavid du Colombier 	char *field[15];
5099a747e4fSDavid du Colombier 	char *dfield[4];
5109a747e4fSDavid du Colombier 	char *cp;
5119a747e4fSDavid du Colombier 	String *s;
5129a747e4fSDavid du Colombier 	int dn, n;
5139a747e4fSDavid du Colombier 	Dir d, *dp;
5149a747e4fSDavid du Colombier 
5159a747e4fSDavid du Colombier 	memset(&d, 0, sizeof(d));
5169a747e4fSDavid du Colombier 
5179a747e4fSDavid du Colombier 	n = getfields(p, field, 15, 1, " \t");
5189a747e4fSDavid du Colombier 	if(n > 2 && strcmp(field[n-2], "->") == 0)
5199a747e4fSDavid du Colombier 		n -= 2;
5209a747e4fSDavid du Colombier 	switch(os){
5219a747e4fSDavid du Colombier 	case TSO:
5229a747e4fSDavid du Colombier 		cp = strchr(field[0], '.');
5239a747e4fSDavid du Colombier 		if(cp){
5249a747e4fSDavid du Colombier 			*cp++ = 0;
5259a747e4fSDavid du Colombier 			s = s_copy(cp);
5269a747e4fSDavid du Colombier 			d.uid = field[0];
5279a747e4fSDavid du Colombier 		} else {
5289a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5299a747e4fSDavid du Colombier 			d.uid = "TSO";
5309a747e4fSDavid du Colombier 		}
5319a747e4fSDavid du Colombier 		d.gid = "TSO";
5329a747e4fSDavid du Colombier 		d.mode = 0666;
5339a747e4fSDavid du Colombier 		d.length = 0;
5349a747e4fSDavid du Colombier 		d.atime = 0;
5359a747e4fSDavid du Colombier 		break;
5369a747e4fSDavid du Colombier 	case OS½:
5379a747e4fSDavid du Colombier 		s = s_copy(field[n-1]);
5389a747e4fSDavid du Colombier 		d.uid = "OS½";
5399a747e4fSDavid du Colombier 		d.gid = d.uid;
5409a747e4fSDavid du Colombier 		d.mode = 0666;
5419a747e4fSDavid du Colombier 		switch(n){
5429a747e4fSDavid du Colombier 		case 5:
5439a747e4fSDavid du Colombier 			if(strcmp(field[1], "DIR") == 0)
5449a747e4fSDavid du Colombier 				d.mode |= DMDIR;
5459a747e4fSDavid du Colombier 			d.length = atoi(field[0]);
5469a747e4fSDavid du Colombier 			dn = getfields(field[2], dfield, 4, 1, "-");
5479a747e4fSDavid du Colombier 			if(dn == 3)
5489a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
5499a747e4fSDavid du Colombier 			break;
5509a747e4fSDavid du Colombier 		}
5519a747e4fSDavid du Colombier 		break;
5529a747e4fSDavid du Colombier 	case Tops:
5539a747e4fSDavid du Colombier 		if(n != 4){ /* tops directory name */
5549a747e4fSDavid du Colombier 			safecpy(topsdir, field[0], sizeof(topsdir));
5559a747e4fSDavid du Colombier 			return 0;
5569a747e4fSDavid du Colombier 		}
5579a747e4fSDavid du Colombier 		s = s_copy(field[3]);
5589a747e4fSDavid du Colombier 		d.length = atoi(field[0]);
5599a747e4fSDavid du Colombier 		d.mode = 0666;
5609a747e4fSDavid du Colombier 		d.uid = "Tops";
5619a747e4fSDavid du Colombier 		d.gid = d.uid;
5629a747e4fSDavid du Colombier 		dn = getfields(field[1], dfield, 4, 1, "-");
5639a747e4fSDavid du Colombier 		if(dn == 3)
5649a747e4fSDavid du Colombier 			d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
5659a747e4fSDavid du Colombier 		else
5669a747e4fSDavid du Colombier 			d.atime = time(0);
5679a747e4fSDavid du Colombier 		break;
5689a747e4fSDavid du Colombier 	case VM:
5699a747e4fSDavid du Colombier 		switch(n){
5709a747e4fSDavid du Colombier 		case 9:
5719a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5729a747e4fSDavid du Colombier 			s_append(s, ".");
5739a747e4fSDavid du Colombier 			s_append(s, field[1]);
5749a747e4fSDavid du Colombier 			d.length = atoi(field[3])*atoi(field[4]);
5759a747e4fSDavid du Colombier 			if(*field[2] == 'F')
5769a747e4fSDavid du Colombier 				d.mode = 0666;
5779a747e4fSDavid du Colombier 			else
5789a747e4fSDavid du Colombier 				d.mode = 0777;
5799a747e4fSDavid du Colombier 			d.uid = "VM";
5809a747e4fSDavid du Colombier 			d.gid = d.uid;
5819a747e4fSDavid du Colombier 			dn = getfields(field[6], dfield, 4, 1, "/-");
5829a747e4fSDavid du Colombier 			if(dn == 3)
5839a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
5849a747e4fSDavid du Colombier 			else
5859a747e4fSDavid du Colombier 				d.atime = time(0);
5869a747e4fSDavid du Colombier 			break;
5879a747e4fSDavid du Colombier 		case 1:
5889a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5899a747e4fSDavid du Colombier 			d.uid = "VM";
5909a747e4fSDavid du Colombier 			d.gid = d.uid;
5919a747e4fSDavid du Colombier 			d.mode = 0777;
5929a747e4fSDavid du Colombier 			d.atime = 0;
5939a747e4fSDavid du Colombier 			break;
5949a747e4fSDavid du Colombier 		default:
5959a747e4fSDavid du Colombier 			return nil;
5969a747e4fSDavid du Colombier 		}
5979a747e4fSDavid du Colombier 		break;
5989a747e4fSDavid du Colombier 	case VMS:
5999a747e4fSDavid du Colombier 		switch(n){
6009a747e4fSDavid du Colombier 		case 6:
6019a747e4fSDavid du Colombier 			for(cp = field[0]; *cp; cp++)
6029a747e4fSDavid du Colombier 				*cp = tolower(*cp);
6039a747e4fSDavid du Colombier 			cp = strchr(field[0], ';');
6049a747e4fSDavid du Colombier 			if(cp)
6059a747e4fSDavid du Colombier 				*cp = 0;
6069a747e4fSDavid du Colombier 			d.mode = 0666;
6079a747e4fSDavid du Colombier 			cp = field[0] + strlen(field[0]) - 4;
6089a747e4fSDavid du Colombier 			if(strcmp(cp, ".dir") == 0){
6099a747e4fSDavid du Colombier 				d.mode |= DMDIR;
6109a747e4fSDavid du Colombier 				*cp = 0;
6119a747e4fSDavid du Colombier 			}
6129a747e4fSDavid du Colombier 			s = s_copy(field[0]);
6139a747e4fSDavid du Colombier 			d.length = atoi(field[1])*512;
6149a747e4fSDavid du Colombier 			field[4][strlen(field[4])-1] = 0;
6159a747e4fSDavid du Colombier 			d.uid = field[4]+1;
6169a747e4fSDavid du Colombier 			d.gid = d.uid;
6179a747e4fSDavid du Colombier 			dn = getfields(field[2], dfield, 4, 1, "/-");
6189a747e4fSDavid du Colombier 			if(dn == 3)
6199a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
6209a747e4fSDavid du Colombier 			else
6219a747e4fSDavid du Colombier 				d.atime = time(0);
6229a747e4fSDavid du Colombier 			break;
6239a747e4fSDavid du Colombier 		default:
6249a747e4fSDavid du Colombier 			return nil;
6259a747e4fSDavid du Colombier 		}
6269a747e4fSDavid du Colombier 		break;
6279a747e4fSDavid du Colombier 	case NetWare:
6289a747e4fSDavid du Colombier 		switch(n){
629ecc2a59cSDavid du Colombier 		case 8:		/* New style */
630ecc2a59cSDavid du Colombier 			s = s_copy(field[7]);
631ecc2a59cSDavid du Colombier 			d.uid = field[2];
632ecc2a59cSDavid du Colombier 			d.gid = d.uid;
633ecc2a59cSDavid du Colombier 			d.mode = nw_mode(field[0][0], field[1]);
634ecc2a59cSDavid du Colombier 			d.length = atoi(field[3]);
635ecc2a59cSDavid du Colombier 			if(strchr(field[6], ':'))
636ecc2a59cSDavid du Colombier 				d.atime = cracktime(field[4], field[5], nil, field[6]);
637ecc2a59cSDavid du Colombier 			else
638ecc2a59cSDavid du Colombier 				d.atime = cracktime(field[4], field[5], field[6], nil);
639ecc2a59cSDavid du Colombier 			break;
6409a747e4fSDavid du Colombier 		case 9:
6419a747e4fSDavid du Colombier 			s = s_copy(field[8]);
6429a747e4fSDavid du Colombier 			d.uid = field[2];
6439a747e4fSDavid du Colombier 			d.gid = d.uid;
6449a747e4fSDavid du Colombier 			d.mode = 0666;
6459a747e4fSDavid du Colombier 			if(*field[0] == 'd')
6469a747e4fSDavid du Colombier 				d.mode |= DMDIR;
6479a747e4fSDavid du Colombier 			d.length = atoi(field[3]);
6489a747e4fSDavid du Colombier 			d.atime = cracktime(field[4], field[5], field[6], field[7]);
6499a747e4fSDavid du Colombier 			break;
6509a747e4fSDavid du Colombier 		case 1:
6519a747e4fSDavid du Colombier 			s = s_copy(field[0]);
6529a747e4fSDavid du Colombier 			d.uid = "none";
6539a747e4fSDavid du Colombier 			d.gid = d.uid;
6549a747e4fSDavid du Colombier 			d.mode = 0777;
6559a747e4fSDavid du Colombier 			d.atime = 0;
6569a747e4fSDavid du Colombier 			break;
6579a747e4fSDavid du Colombier 		default:
6589a747e4fSDavid du Colombier 			return nil;
6599a747e4fSDavid du Colombier 		}
6609a747e4fSDavid du Colombier 		break;
6619a747e4fSDavid du Colombier 	case Unix:
6629a747e4fSDavid du Colombier 	case Plan9:
6639a747e4fSDavid du Colombier 	default:
6649a747e4fSDavid du Colombier 		switch(n){
6659a747e4fSDavid du Colombier 		case 8:		/* ls -l */
6669a747e4fSDavid du Colombier 			s = s_copy(field[7]);
6679a747e4fSDavid du Colombier 			d.uid = field[2];
6689a747e4fSDavid du Colombier 			d.gid = d.uid;
6699a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6709a747e4fSDavid du Colombier 			d.length = atoi(field[3]);
6719a747e4fSDavid du Colombier 			if(strchr(field[6], ':'))
6729a747e4fSDavid du Colombier 				d.atime = cracktime(field[4], field[5], 0, field[6]);
6739a747e4fSDavid du Colombier 			else
6749a747e4fSDavid du Colombier 				d.atime = cracktime(field[4], field[5], field[6], 0);
6759a747e4fSDavid du Colombier 			break;
6769a747e4fSDavid du Colombier 		case 9:		/* ls -lg */
6779a747e4fSDavid du Colombier 			s = s_copy(field[8]);
6789a747e4fSDavid du Colombier 			d.uid = field[2];
6799a747e4fSDavid du Colombier 			d.gid = field[3];
6809a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6819a747e4fSDavid du Colombier 			d.length = atoi(field[4]);
6829a747e4fSDavid du Colombier 			if(strchr(field[7], ':'))
6839a747e4fSDavid du Colombier 				d.atime = cracktime(field[5], field[6], 0, field[7]);
6849a747e4fSDavid du Colombier 			else
6859a747e4fSDavid du Colombier 				d.atime = cracktime(field[5], field[6], field[7], 0);
6869a747e4fSDavid du Colombier 			break;
6879a747e4fSDavid du Colombier 		case 10:	/* plan 9 */
6889a747e4fSDavid du Colombier 			s = s_copy(field[9]);
6899a747e4fSDavid du Colombier 			d.uid = field[3];
6909a747e4fSDavid du Colombier 			d.gid = field[4];
6919a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6929a747e4fSDavid du Colombier 			d.length = atoi(field[5]);
6939a747e4fSDavid du Colombier 			if(strchr(field[8], ':'))
6949a747e4fSDavid du Colombier 				d.atime = cracktime(field[6], field[7], 0, field[8]);
6959a747e4fSDavid du Colombier 			else
6969a747e4fSDavid du Colombier 				d.atime = cracktime(field[6], field[7], field[8], 0);
6979a747e4fSDavid du Colombier 			break;
6989a747e4fSDavid du Colombier 		case 4:		/* a Windows_NT version */
6999a747e4fSDavid du Colombier 			s = s_copy(field[3]);
7009a747e4fSDavid du Colombier 			d.uid = "NT";
7019a747e4fSDavid du Colombier 			d.gid = d.uid;
7029a747e4fSDavid du Colombier 			if(strcmp("<DIR>", field[2]) == 0){
7039a747e4fSDavid du Colombier 				d.length = 0;
7049a747e4fSDavid du Colombier 				d.mode = DMDIR|0777;
7059a747e4fSDavid du Colombier 			} else {
7069a747e4fSDavid du Colombier 				d.mode = 0666;
7079a747e4fSDavid du Colombier 				d.length = atoi(field[2]);
7089a747e4fSDavid du Colombier 			}
7099a747e4fSDavid du Colombier 			dn = getfields(field[0], dfield, 4, 1, "/-");
7109a747e4fSDavid du Colombier 			if(dn == 3)
7119a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
7129a747e4fSDavid du Colombier 			break;
7139a747e4fSDavid du Colombier 		case 1:
7149a747e4fSDavid du Colombier 			s = s_copy(field[0]);
7159a747e4fSDavid du Colombier 			d.uid = "none";
7169a747e4fSDavid du Colombier 			d.gid = d.uid;
7179a747e4fSDavid du Colombier 			d.mode = 0777;
7189a747e4fSDavid du Colombier 			d.atime = 0;
7199a747e4fSDavid du Colombier 			break;
7209a747e4fSDavid du Colombier 		default:
7219a747e4fSDavid du Colombier 			return nil;
7229a747e4fSDavid du Colombier 		}
7239a747e4fSDavid du Colombier 	}
7249a747e4fSDavid du Colombier 	d.muid = d.uid;
7259a747e4fSDavid du Colombier 	d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
7269a747e4fSDavid du Colombier 	d.mtime = d.atime;
7279a747e4fSDavid du Colombier 	if(ext && (d.qid.type & QTDIR) == 0)
7289a747e4fSDavid du Colombier 		s_append(s, ext);
7299a747e4fSDavid du Colombier 	d.name = s_to_c(s);
7309a747e4fSDavid du Colombier 
7319a747e4fSDavid du Colombier 	/* allocate a freeable dir structure */
7329a747e4fSDavid du Colombier 	dp = reallocdir(&d, 0);
7339a747e4fSDavid du Colombier 	*remname = s;
7349a747e4fSDavid du Colombier 
7359a747e4fSDavid du Colombier 	return dp;
7369a747e4fSDavid du Colombier }
7379a747e4fSDavid du Colombier 
7389a747e4fSDavid du Colombier /*
7399a747e4fSDavid du Colombier  *  probe files in a directory to see if they are directories
7409a747e4fSDavid du Colombier  */
7419a747e4fSDavid du Colombier /*
7429a747e4fSDavid du Colombier  *  read a remote directory
7439a747e4fSDavid du Colombier  */
7449a747e4fSDavid du Colombier int
7459a747e4fSDavid du Colombier readdir(Node *node)
7469a747e4fSDavid du Colombier {
7479a747e4fSDavid du Colombier 	Biobuf *bp;
7489a747e4fSDavid du Colombier 	char *line;
7499a747e4fSDavid du Colombier 	Node *np;
7509a747e4fSDavid du Colombier 	Dir *d;
7519a747e4fSDavid du Colombier 	long n;
7529a747e4fSDavid du Colombier 	int tries, x, files;
7539a747e4fSDavid du Colombier 	static int uselist;
7549a747e4fSDavid du Colombier 	int usenlist;
7559a747e4fSDavid du Colombier 	String *remname;
7569a747e4fSDavid du Colombier 
7579a747e4fSDavid du Colombier 	if(changedir(node) < 0)
7589a747e4fSDavid du Colombier 		return -1;
7599a747e4fSDavid du Colombier 
7609a747e4fSDavid du Colombier 	usenlist = 0;
7619a747e4fSDavid du Colombier 	for(tries = 0; tries < 3; tries++){
7629a747e4fSDavid du Colombier 		if(usenlist || usenlst)
7639a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "NLST", nil);
7649a747e4fSDavid du Colombier 		else if(os == Unix && !uselist)
7659a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "LIST -l", nil);
7669a747e4fSDavid du Colombier 		else
7679a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "LIST", nil);
7689a747e4fSDavid du Colombier 		switch(x){
7699a747e4fSDavid du Colombier 		case Extra:
7709a747e4fSDavid du Colombier 			break;
7719a747e4fSDavid du Colombier /*		case TempFail:
7729a747e4fSDavid du Colombier 			continue;
7739a747e4fSDavid du Colombier */
7749a747e4fSDavid du Colombier 		default:
7759a747e4fSDavid du Colombier 			if(os == Unix && uselist == 0){
7769a747e4fSDavid du Colombier 				uselist = 1;
7779a747e4fSDavid du Colombier 				continue;
7789a747e4fSDavid du Colombier 			}
7799a747e4fSDavid du Colombier 			return seterr(nosuchfile);
7809a747e4fSDavid du Colombier 		}
7819a747e4fSDavid du Colombier 		files = 0;
7829a747e4fSDavid du Colombier 		while(line = Brdline(bp, '\n')){
7839a747e4fSDavid du Colombier 			n = Blinelen(bp);
7849a747e4fSDavid du Colombier 			if(debug)
7859a747e4fSDavid du Colombier 				write(2, line, n);
7869a747e4fSDavid du Colombier 			if(n > 1 && line[n-2] == '\r')
7879a747e4fSDavid du Colombier 				n--;
7889a747e4fSDavid du Colombier 			line[n - 1] = 0;
7899a747e4fSDavid du Colombier 
7909a747e4fSDavid du Colombier 			d = crackdir(line, &remname);
7919a747e4fSDavid du Colombier 			if(d == nil)
7929a747e4fSDavid du Colombier 				continue;
7939a747e4fSDavid du Colombier 			files++;
7949a747e4fSDavid du Colombier 			np = extendpath(node, remname);
7959a747e4fSDavid du Colombier 			d->qid.path = np->d->qid.path;
7969a747e4fSDavid du Colombier 			d->qid.vers = np->d->qid.vers;
7979a747e4fSDavid du Colombier 			d->type = np->d->type;
7989a747e4fSDavid du Colombier 			d->dev = 1;			/* mark node as valid */
7999a747e4fSDavid du Colombier 			if(os == MVS && node == remroot){
8009a747e4fSDavid du Colombier 				d->qid.type = QTDIR;
8019a747e4fSDavid du Colombier 				d->mode |= DMDIR;
8029a747e4fSDavid du Colombier 			}
8039a747e4fSDavid du Colombier 			free(np->d);
8049a747e4fSDavid du Colombier 			np->d = d;
8059a747e4fSDavid du Colombier 		}
8069a747e4fSDavid du Colombier 		close(Bfildes(bp));
8079a747e4fSDavid du Colombier 
8089a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
8099a747e4fSDavid du Colombier 		case Success:
8109a747e4fSDavid du Colombier 			if(files == 0 && !usenlst && !usenlist){
8119a747e4fSDavid du Colombier 				usenlist = 1;
8129a747e4fSDavid du Colombier 				continue;
8139a747e4fSDavid du Colombier 			}
8149a747e4fSDavid du Colombier 			if(files && usenlist)
8159a747e4fSDavid du Colombier 				usenlst = 1;
8169a747e4fSDavid du Colombier 			if(usenlst)
8179a747e4fSDavid du Colombier 				node->chdirunknown = 1;
8189a747e4fSDavid du Colombier 			return 0;
8199a747e4fSDavid du Colombier 		case TempFail:
8209a747e4fSDavid du Colombier 			break;
8219a747e4fSDavid du Colombier 		default:
8229a747e4fSDavid du Colombier 			return seterr(nosuchfile);
8239a747e4fSDavid du Colombier 		}
8249a747e4fSDavid du Colombier 	}
8259a747e4fSDavid du Colombier 	return seterr(nosuchfile);
8269a747e4fSDavid du Colombier }
8279a747e4fSDavid du Colombier 
8289a747e4fSDavid du Colombier /*
8299a747e4fSDavid du Colombier  *  create a remote directory
8309a747e4fSDavid du Colombier  */
8319a747e4fSDavid du Colombier int
8329a747e4fSDavid du Colombier createdir(Node *node)
8339a747e4fSDavid du Colombier {
8349a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
8359a747e4fSDavid du Colombier 		return -1;
8369a747e4fSDavid du Colombier 
8379a747e4fSDavid du Colombier 	sendrequest("MKD", node->d->name);
8389a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
8399a747e4fSDavid du Colombier 		return -1;
8409a747e4fSDavid du Colombier 	return 0;
8419a747e4fSDavid du Colombier }
8429a747e4fSDavid du Colombier 
8439a747e4fSDavid du Colombier /*
8449a747e4fSDavid du Colombier  *  change to a remote directory.
8459a747e4fSDavid du Colombier  */
8469a747e4fSDavid du Colombier int
8479a747e4fSDavid du Colombier changedir(Node *node)
8489a747e4fSDavid du Colombier {
8499a747e4fSDavid du Colombier 	Node *to;
8509a747e4fSDavid du Colombier 	String *cdpath;
8519a747e4fSDavid du Colombier 
8529a747e4fSDavid du Colombier 	to = node;
8539a747e4fSDavid du Colombier 	if(to == remdir)
8549a747e4fSDavid du Colombier 		return 0;
8559a747e4fSDavid du Colombier 
8569a747e4fSDavid du Colombier 	/* build an absolute path */
8579a747e4fSDavid du Colombier 	switch(os){
8589a747e4fSDavid du Colombier 	case Tops:
8599a747e4fSDavid du Colombier 	case VM:
8609a747e4fSDavid du Colombier 		switch(node->depth){
8619a747e4fSDavid du Colombier 		case 0:
8629a747e4fSDavid du Colombier 			remdir = node;
8639a747e4fSDavid du Colombier 			return 0;
8649a747e4fSDavid du Colombier 		case 1:
8659a747e4fSDavid du Colombier 			cdpath = s_clone(node->remname);
8669a747e4fSDavid du Colombier 			break;
8679a747e4fSDavid du Colombier 		default:
8689a747e4fSDavid du Colombier 			return seterr(nosuchfile);
8699a747e4fSDavid du Colombier 		}
8709a747e4fSDavid du Colombier 		break;
8719a747e4fSDavid du Colombier 	case VMS:
8729a747e4fSDavid du Colombier 		switch(node->depth){
8739a747e4fSDavid du Colombier 		case 0:
8749a747e4fSDavid du Colombier 			remdir = node;
8759a747e4fSDavid du Colombier 			return 0;
8769a747e4fSDavid du Colombier 		default:
8779a747e4fSDavid du Colombier 			cdpath = s_new();
8789a747e4fSDavid du Colombier 			vmspath(node, cdpath);
8799a747e4fSDavid du Colombier 		}
8809a747e4fSDavid du Colombier 		break;
8819a747e4fSDavid du Colombier 	case MVS:
8829a747e4fSDavid du Colombier 		if(node->depth == 0)
8839a747e4fSDavid du Colombier 			cdpath = s_clone(remrootpath);
8849a747e4fSDavid du Colombier 		else{
8859a747e4fSDavid du Colombier 			cdpath = s_new();
8869a747e4fSDavid du Colombier 			mvspath(node, cdpath);
8879a747e4fSDavid du Colombier 		}
8889a747e4fSDavid du Colombier 		break;
8899a747e4fSDavid du Colombier 	default:
8909a747e4fSDavid du Colombier 		if(node->depth == 0)
8919a747e4fSDavid du Colombier 			cdpath = s_clone(remrootpath);
8929a747e4fSDavid du Colombier 		else{
8939a747e4fSDavid du Colombier 			cdpath = s_new();
8949a747e4fSDavid du Colombier 			unixpath(node, cdpath);
8959a747e4fSDavid du Colombier 		}
8969a747e4fSDavid du Colombier 		break;
8979a747e4fSDavid du Colombier 	}
8989a747e4fSDavid du Colombier 
8999a747e4fSDavid du Colombier 	uncachedir(remdir, 0);
9009a747e4fSDavid du Colombier 
9019a747e4fSDavid du Colombier 	/*
9029a747e4fSDavid du Colombier 	 *  connect, if we need a password (Incomplete)
9039a747e4fSDavid du Colombier 	 *  act like it worked (best we can do).
9049a747e4fSDavid du Colombier 	 */
9059a747e4fSDavid du Colombier 	sendrequest("CWD", s_to_c(cdpath));
9069a747e4fSDavid du Colombier 	s_free(cdpath);
9079a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
9089a747e4fSDavid du Colombier 	case Success:
9099a747e4fSDavid du Colombier 	case Incomplete:
9109a747e4fSDavid du Colombier 		remdir = node;
9119a747e4fSDavid du Colombier 		return 0;
9129a747e4fSDavid du Colombier 	default:
9139a747e4fSDavid du Colombier 		return seterr(nosuchfile);
9149a747e4fSDavid du Colombier 	}
9159a747e4fSDavid du Colombier }
9169a747e4fSDavid du Colombier 
9179a747e4fSDavid du Colombier /*
9189a747e4fSDavid du Colombier  *  read a remote file
9199a747e4fSDavid du Colombier  */
9209a747e4fSDavid du Colombier int
9219a747e4fSDavid du Colombier readfile1(Node *node)
9229a747e4fSDavid du Colombier {
9239a747e4fSDavid du Colombier 	Biobuf *bp;
9249a747e4fSDavid du Colombier 	char buf[4*1024];
9259a747e4fSDavid du Colombier 	long off;
9269a747e4fSDavid du Colombier 	int n;
9279a747e4fSDavid du Colombier 	int tries;
9289a747e4fSDavid du Colombier 
9299a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
9309a747e4fSDavid du Colombier 		return -1;
9319a747e4fSDavid du Colombier 
9329a747e4fSDavid du Colombier 	for(tries = 0; tries < 4; tries++){
9339a747e4fSDavid du Colombier 		switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
9349a747e4fSDavid du Colombier 		case Extra:
9359a747e4fSDavid du Colombier 			break;
9369a747e4fSDavid du Colombier 		case TempFail:
9379a747e4fSDavid du Colombier 			continue;
9389a747e4fSDavid du Colombier 		default:
9399a747e4fSDavid du Colombier 			return seterr(nosuchfile);
9409a747e4fSDavid du Colombier 		}
9419a747e4fSDavid du Colombier 		off = 0;
9429a747e4fSDavid du Colombier 		while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
9439a747e4fSDavid du Colombier 			if(filewrite(node, buf, off, n) != n){
9449a747e4fSDavid du Colombier 				off = -1;
9459a747e4fSDavid du Colombier 				break;
9469a747e4fSDavid du Colombier 			}
9479a747e4fSDavid du Colombier 			off += n;
9489a747e4fSDavid du Colombier 		}
9499a747e4fSDavid du Colombier 		if(off < 0)
9509a747e4fSDavid du Colombier 			return -1;
9519a747e4fSDavid du Colombier 
9529a747e4fSDavid du Colombier 		/* make sure a file gets created even for a zero length file */
9539a747e4fSDavid du Colombier 		if(off == 0)
9549a747e4fSDavid du Colombier 			filewrite(node, buf, 0, 0);
9559a747e4fSDavid du Colombier 
9569a747e4fSDavid du Colombier 		close(Bfildes(bp));
9579a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
9589a747e4fSDavid du Colombier 		case Success:
9599a747e4fSDavid du Colombier 			return off;
9609a747e4fSDavid du Colombier 		case TempFail:
9619a747e4fSDavid du Colombier 			continue;
9629a747e4fSDavid du Colombier 		default:
9639a747e4fSDavid du Colombier 			return seterr(nosuchfile);
9649a747e4fSDavid du Colombier 		}
9659a747e4fSDavid du Colombier 	}
9669a747e4fSDavid du Colombier 	return seterr(nosuchfile);
9679a747e4fSDavid du Colombier }
9689a747e4fSDavid du Colombier 
9699a747e4fSDavid du Colombier int
9709a747e4fSDavid du Colombier readfile(Node *node)
9719a747e4fSDavid du Colombier {
9729a747e4fSDavid du Colombier 	int rv, inimage;
9739a747e4fSDavid du Colombier 
9749a747e4fSDavid du Colombier 	switch(os){
9759a747e4fSDavid du Colombier 	case MVS:
9769a747e4fSDavid du Colombier 	case Plan9:
9779a747e4fSDavid du Colombier 	case Tops:
9789a747e4fSDavid du Colombier 	case TSO:
9799a747e4fSDavid du Colombier 		inimage = 0;
9809a747e4fSDavid du Colombier 		break;
9819a747e4fSDavid du Colombier 	default:
9829a747e4fSDavid du Colombier 		inimage = 1;
9839a747e4fSDavid du Colombier 		image();
9849a747e4fSDavid du Colombier 		break;
9859a747e4fSDavid du Colombier 	}
9869a747e4fSDavid du Colombier 
9879a747e4fSDavid du Colombier 	rv = readfile1(node);
9889a747e4fSDavid du Colombier 
9899a747e4fSDavid du Colombier 	if(inimage)
9909a747e4fSDavid du Colombier 		ascii();
9919a747e4fSDavid du Colombier 	return rv;
9929a747e4fSDavid du Colombier }
9939a747e4fSDavid du Colombier 
9949a747e4fSDavid du Colombier /*
9959a747e4fSDavid du Colombier  *  write back a file
9969a747e4fSDavid du Colombier  */
9979a747e4fSDavid du Colombier int
9989a747e4fSDavid du Colombier createfile1(Node *node)
9999a747e4fSDavid du Colombier {
10009a747e4fSDavid du Colombier 	Biobuf *bp;
10019a747e4fSDavid du Colombier 	char buf[4*1024];
10029a747e4fSDavid du Colombier 	long off;
10039a747e4fSDavid du Colombier 	int n;
10049a747e4fSDavid du Colombier 
10059a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
10069a747e4fSDavid du Colombier 		return -1;
10079a747e4fSDavid du Colombier 
10089a747e4fSDavid du Colombier 	if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
10099a747e4fSDavid du Colombier 		return -1;
10109a747e4fSDavid du Colombier 	for(off = 0; ; off += n){
10119a747e4fSDavid du Colombier 		n = fileread(node, buf, off, sizeof(buf));
10129a747e4fSDavid du Colombier 		if(n <= 0)
10139a747e4fSDavid du Colombier 			break;
10149a747e4fSDavid du Colombier 		write(Bfildes(bp), buf, n);
10159a747e4fSDavid du Colombier 	}
10169a747e4fSDavid du Colombier 	close(Bfildes(bp));
101705a04ac5SDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
101805a04ac5SDavid du Colombier 		return -1;
10199a747e4fSDavid du Colombier 	return off;
10209a747e4fSDavid du Colombier }
10219a747e4fSDavid du Colombier 
10229a747e4fSDavid du Colombier int
10239a747e4fSDavid du Colombier createfile(Node *node)
10249a747e4fSDavid du Colombier {
10259a747e4fSDavid du Colombier 	int rv;
10269a747e4fSDavid du Colombier 
10279a747e4fSDavid du Colombier 	switch(os){
10289a747e4fSDavid du Colombier 	case Plan9:
10299a747e4fSDavid du Colombier 	case Tops:
10309a747e4fSDavid du Colombier 		break;
10319a747e4fSDavid du Colombier 	default:
10329a747e4fSDavid du Colombier 		image();
10339a747e4fSDavid du Colombier 		break;
10349a747e4fSDavid du Colombier 	}
10359a747e4fSDavid du Colombier 	rv = createfile1(node);
10369a747e4fSDavid du Colombier 	switch(os){
10379a747e4fSDavid du Colombier 	case Plan9:
10389a747e4fSDavid du Colombier 	case Tops:
10399a747e4fSDavid du Colombier 		break;
10409a747e4fSDavid du Colombier 	default:
10419a747e4fSDavid du Colombier 		ascii();
10429a747e4fSDavid du Colombier 		break;
10439a747e4fSDavid du Colombier 	}
10449a747e4fSDavid du Colombier 	return rv;
10459a747e4fSDavid du Colombier }
10469a747e4fSDavid du Colombier 
10479a747e4fSDavid du Colombier /*
10489a747e4fSDavid du Colombier  *  remove a remote file
10499a747e4fSDavid du Colombier  */
10509a747e4fSDavid du Colombier int
10519a747e4fSDavid du Colombier removefile(Node *node)
10529a747e4fSDavid du Colombier {
10539a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
10549a747e4fSDavid du Colombier 		return -1;
10559a747e4fSDavid du Colombier 
10569a747e4fSDavid du Colombier 	sendrequest("DELE", s_to_c(node->remname));
10579a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
10589a747e4fSDavid du Colombier 		return -1;
10599a747e4fSDavid du Colombier 	return 0;
10609a747e4fSDavid du Colombier }
10619a747e4fSDavid du Colombier 
10629a747e4fSDavid du Colombier /*
10639a747e4fSDavid du Colombier  *  remove a remote directory
10649a747e4fSDavid du Colombier  */
10659a747e4fSDavid du Colombier int
10669a747e4fSDavid du Colombier removedir(Node *node)
10679a747e4fSDavid du Colombier {
10689a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
10699a747e4fSDavid du Colombier 		return -1;
10709a747e4fSDavid du Colombier 
10719a747e4fSDavid du Colombier 	sendrequest("RMD", s_to_c(node->remname));
10729a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
10739a747e4fSDavid du Colombier 		return -1;
10749a747e4fSDavid du Colombier 	return 0;
10759a747e4fSDavid du Colombier }
10769a747e4fSDavid du Colombier 
10779a747e4fSDavid du Colombier /*
10789a747e4fSDavid du Colombier  *  tell remote that we're exiting and then do it
10799a747e4fSDavid du Colombier  */
10809a747e4fSDavid du Colombier void
10819a747e4fSDavid du Colombier quit(void)
10829a747e4fSDavid du Colombier {
10839a747e4fSDavid du Colombier 	sendrequest("QUIT", nil);
10849a747e4fSDavid du Colombier 	getreply(&ctlin, msg, sizeof(msg), 0);
10859a747e4fSDavid du Colombier 	exits(0);
10869a747e4fSDavid du Colombier }
10879a747e4fSDavid du Colombier 
10889a747e4fSDavid du Colombier /*
10899a747e4fSDavid du Colombier  *  send a request
10909a747e4fSDavid du Colombier  */
10919a747e4fSDavid du Colombier static void
10929a747e4fSDavid du Colombier sendrequest(char *a, char *b)
10939a747e4fSDavid du Colombier {
10949a747e4fSDavid du Colombier 	char buf[2*1024];
10959a747e4fSDavid du Colombier 	int n;
10969a747e4fSDavid du Colombier 
10979a747e4fSDavid du Colombier 	n = strlen(a)+2+1;
10989a747e4fSDavid du Colombier 	if(b != nil)
10999a747e4fSDavid du Colombier 		n += strlen(b)+1;
11009a747e4fSDavid du Colombier 	if(n >= sizeof(buf))
11019a747e4fSDavid du Colombier 		fatal("proto request too long");
11029a747e4fSDavid du Colombier 	strcpy(buf, a);
11039a747e4fSDavid du Colombier 	if(b != nil){
11049a747e4fSDavid du Colombier 		strcat(buf, " ");
11059a747e4fSDavid du Colombier 		strcat(buf, b);
11069a747e4fSDavid du Colombier 	}
11079a747e4fSDavid du Colombier 	strcat(buf, "\r\n");
11089a747e4fSDavid du Colombier 	n = strlen(buf);
11099a747e4fSDavid du Colombier 	if(write(ctlfd, buf, n) != n)
11109a747e4fSDavid du Colombier 		fatal("remote side hung up");
11119a747e4fSDavid du Colombier 	if(debug)
11129a747e4fSDavid du Colombier 		write(2, buf, n);
11139a747e4fSDavid du Colombier 	lastsend = time(0);
11149a747e4fSDavid du Colombier }
11159a747e4fSDavid du Colombier 
11169a747e4fSDavid du Colombier /*
11179a747e4fSDavid du Colombier  *  replies codes are in the range [100, 999] and may contain multiple lines of
11189a747e4fSDavid du Colombier  *  continuation.
11199a747e4fSDavid du Colombier  */
11209a747e4fSDavid du Colombier static int
11219a747e4fSDavid du Colombier getreply(Biobuf *bp, char *msg, int len, int printreply)
11229a747e4fSDavid du Colombier {
11239a747e4fSDavid du Colombier 	char *line;
1124d9306527SDavid du Colombier 	char *p;
11259a747e4fSDavid du Colombier 	int rv;
11269a747e4fSDavid du Colombier 	int i, n;
11279a747e4fSDavid du Colombier 
11289a747e4fSDavid du Colombier 	while(line = Brdline(bp, '\n')){
11299a747e4fSDavid du Colombier 		/* add line to message buffer, strip off \r */
11309a747e4fSDavid du Colombier 		n = Blinelen(bp);
11319a747e4fSDavid du Colombier 		if(n > 1 && line[n-2] == '\r'){
11329a747e4fSDavid du Colombier 			n--;
11339a747e4fSDavid du Colombier 			line[n-1] = '\n';
11349a747e4fSDavid du Colombier 		}
11359a747e4fSDavid du Colombier 		if(printreply && !quiet)
11369a747e4fSDavid du Colombier 			write(1, line, n);
11379a747e4fSDavid du Colombier 		else if(debug)
11389a747e4fSDavid du Colombier 			write(2, line, n);
11399a747e4fSDavid du Colombier 		if(n > len - 1)
11409a747e4fSDavid du Colombier 			i = len - 1;
11419a747e4fSDavid du Colombier 		else
11429a747e4fSDavid du Colombier 			i = n;
11439a747e4fSDavid du Colombier 		if(i > 0){
11449a747e4fSDavid du Colombier 			memmove(msg, line, i);
11459a747e4fSDavid du Colombier 			msg += i;
11469a747e4fSDavid du Colombier 			len -= i;
11479a747e4fSDavid du Colombier 			*msg = 0;
11489a747e4fSDavid du Colombier 		}
11499a747e4fSDavid du Colombier 
11509a747e4fSDavid du Colombier 		/* stop if not a continuation */
1151d9306527SDavid du Colombier 		rv = strtol(line, &p, 10);
1152d9306527SDavid du Colombier 		if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
11539a747e4fSDavid du Colombier 			return rv/100;
11549a747e4fSDavid du Colombier 
11559a747e4fSDavid du Colombier 		/* tell user about continuations */
11569a747e4fSDavid du Colombier 		if(!debug && !quiet && !printreply)
11579a747e4fSDavid du Colombier 			write(2, line, n);
11589a747e4fSDavid du Colombier 	}
11599a747e4fSDavid du Colombier 
11609a747e4fSDavid du Colombier 	fatal("remote side closed connection");
11619a747e4fSDavid du Colombier 	return 0;
11629a747e4fSDavid du Colombier }
11639a747e4fSDavid du Colombier 
11649a747e4fSDavid du Colombier /*
11659a747e4fSDavid du Colombier  *  Announce on a local port and tell its address to the the remote side
11669a747e4fSDavid du Colombier  */
11679a747e4fSDavid du Colombier static int
11689a747e4fSDavid du Colombier port(void)
11699a747e4fSDavid du Colombier {
11709a747e4fSDavid du Colombier 	char buf[256];
11719a747e4fSDavid du Colombier 	int n, fd;
11729a747e4fSDavid du Colombier 	char *ptr;
11739a747e4fSDavid du Colombier 	uchar ipaddr[IPaddrlen];
11749a747e4fSDavid du Colombier 	int port;
11759a747e4fSDavid du Colombier 
11769a747e4fSDavid du Colombier 	/* get a channel to listen on, let kernel pick the port number */
11779a747e4fSDavid du Colombier 	sprint(buf, "%s!*!0", net);
11789a747e4fSDavid du Colombier 	listenfd = announce(buf, netdir);
11799a747e4fSDavid du Colombier 	if(listenfd < 0)
11809a747e4fSDavid du Colombier 		return seterr("can't announce");
11819a747e4fSDavid du Colombier 
11829a747e4fSDavid du Colombier 	/* get the local address and port number */
11839a747e4fSDavid du Colombier 	sprint(buf, "%s/local", netdir);
11849a747e4fSDavid du Colombier 	fd = open(buf, OREAD);
11859a747e4fSDavid du Colombier 	if(fd < 0)
11869a747e4fSDavid du Colombier 		return seterr("opening %s: %r", buf);
11879a747e4fSDavid du Colombier 	n = read(fd, buf, sizeof(buf)-1);
11889a747e4fSDavid du Colombier 	close(fd);
11899a747e4fSDavid du Colombier 	if(n <= 0)
11909a747e4fSDavid du Colombier 		return seterr("opening %s/local: %r", netdir);
11919a747e4fSDavid du Colombier 	buf[n] = 0;
11929a747e4fSDavid du Colombier 	ptr = strchr(buf, ' ');
11939a747e4fSDavid du Colombier 	if(ptr)
11949a747e4fSDavid du Colombier 		*ptr = 0;
11959a747e4fSDavid du Colombier 	ptr = strchr(buf, '!')+1;
11969a747e4fSDavid du Colombier 	port = atoi(ptr);
11979a747e4fSDavid du Colombier 
11986d0d1481SDavid du Colombier 	memset(ipaddr, 0, IPaddrlen);
11996d0d1481SDavid du Colombier 	if (*net){
12006d0d1481SDavid du Colombier 		strcpy(buf, net);
12016d0d1481SDavid du Colombier 		ptr = strchr(buf +1, '/');
12026d0d1481SDavid du Colombier 		if (ptr)
12036d0d1481SDavid du Colombier 			*ptr = 0;
12046d0d1481SDavid du Colombier 		myipaddr(ipaddr, buf);
12056d0d1481SDavid du Colombier 	}
12066d0d1481SDavid du Colombier 
12079a747e4fSDavid du Colombier 	/* tell remote side */
12089a747e4fSDavid du Colombier 	sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
12099a747e4fSDavid du Colombier 		ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
12109a747e4fSDavid du Colombier 	sendrequest(buf, nil);
12119a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
12129a747e4fSDavid du Colombier 		return seterr(msg);
12139a747e4fSDavid du Colombier 	return 0;
12149a747e4fSDavid du Colombier }
12159a747e4fSDavid du Colombier 
12169a747e4fSDavid du Colombier /*
12179a747e4fSDavid du Colombier  *  have server call back for a data connection
12189a747e4fSDavid du Colombier  */
12199a747e4fSDavid du Colombier static int
12209a747e4fSDavid du Colombier active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
12219a747e4fSDavid du Colombier {
12229a747e4fSDavid du Colombier 	int cfd, dfd, rv;
12239a747e4fSDavid du Colombier 	char newdir[Maxpath];
12249a747e4fSDavid du Colombier 	char datafile[Maxpath + 6];
1225*1269a55eSDavid du Colombier 	TLSconn conn;
12269a747e4fSDavid du Colombier 
12279a747e4fSDavid du Colombier 	if(port() < 0)
12289a747e4fSDavid du Colombier 		return TempFail;
12299a747e4fSDavid du Colombier 
12309a747e4fSDavid du Colombier 	sendrequest(cmda, cmdb);
12319a747e4fSDavid du Colombier 
12329a747e4fSDavid du Colombier 	rv = getreply(&ctlin, msg, sizeof(msg), 0);
12339a747e4fSDavid du Colombier 	if(rv != Extra){
12349a747e4fSDavid du Colombier 		close(listenfd);
12359a747e4fSDavid du Colombier 		return rv;
12369a747e4fSDavid du Colombier 	}
12379a747e4fSDavid du Colombier 
12389a747e4fSDavid du Colombier 	/* wait for a new call */
12399a747e4fSDavid du Colombier 	cfd = listen(netdir, newdir);
12409a747e4fSDavid du Colombier 	if(cfd < 0)
12419a747e4fSDavid du Colombier 		fatal("waiting for data connection");
12429a747e4fSDavid du Colombier 	close(listenfd);
12439a747e4fSDavid du Colombier 
12449a747e4fSDavid du Colombier 	/* open it's data connection and close the control connection */
12459a747e4fSDavid du Colombier 	sprint(datafile, "%s/data", newdir);
12469a747e4fSDavid du Colombier 	dfd = open(datafile, ORDWR);
12479a747e4fSDavid du Colombier 	close(cfd);
12489a747e4fSDavid du Colombier 	if(dfd < 0)
12499a747e4fSDavid du Colombier 		fatal("opening data connection");
1250*1269a55eSDavid du Colombier 
1251*1269a55eSDavid du Colombier 	if(usetls){
1252*1269a55eSDavid du Colombier 		memset(&conn, 0, sizeof(conn));
1253*1269a55eSDavid du Colombier 		dfd = tlsClient(dfd, &conn);
1254*1269a55eSDavid du Colombier 		if(dfd < 0)
1255*1269a55eSDavid du Colombier 			fatal("starting tls: %r");
1256*1269a55eSDavid du Colombier 		free(conn.cert);
1257*1269a55eSDavid du Colombier 	}
1258*1269a55eSDavid du Colombier 
12599a747e4fSDavid du Colombier 	Binit(&dbuf, dfd, mode);
12609a747e4fSDavid du Colombier 	*bpp = &dbuf;
12619a747e4fSDavid du Colombier 	return Extra;
12629a747e4fSDavid du Colombier }
12639a747e4fSDavid du Colombier 
12649a747e4fSDavid du Colombier /*
12659a747e4fSDavid du Colombier  *  call out for a data connection
12669a747e4fSDavid du Colombier  */
12679a747e4fSDavid du Colombier static int
12689a747e4fSDavid du Colombier passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
12699a747e4fSDavid du Colombier {
12709a747e4fSDavid du Colombier 	char msg[1024];
12719a747e4fSDavid du Colombier 	char ds[1024];
12729a747e4fSDavid du Colombier 	char *f[6];
12739a747e4fSDavid du Colombier 	char *p;
12749a747e4fSDavid du Colombier 	int x, fd;
1275*1269a55eSDavid du Colombier 	TLSconn conn;
12769a747e4fSDavid du Colombier 
12779a747e4fSDavid du Colombier 	if(nopassive)
12789a747e4fSDavid du Colombier 		return Impossible;
12799a747e4fSDavid du Colombier 
12809a747e4fSDavid du Colombier 	sendrequest("PASV", nil);
12819a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
12829a747e4fSDavid du Colombier 		nopassive = 1;
12839a747e4fSDavid du Colombier 		return Impossible;
12849a747e4fSDavid du Colombier 	}
12859a747e4fSDavid du Colombier 
12869a747e4fSDavid du Colombier 	/* get address and port number from reply, this is AI */
12879a747e4fSDavid du Colombier 	p = strchr(msg, '(');
12889a747e4fSDavid du Colombier 	if(p == 0){
12899a747e4fSDavid du Colombier 		for(p = msg+3; *p; p++)
12909a747e4fSDavid du Colombier 			if(isdigit(*p))
12919a747e4fSDavid du Colombier 				break;
12929a747e4fSDavid du Colombier 	} else
12939a747e4fSDavid du Colombier 		p++;
12949a747e4fSDavid du Colombier 	if(getfields(p, f, 6, 0, ",") < 6){
12959a747e4fSDavid du Colombier 		if(debug)
12969a747e4fSDavid du Colombier 			fprint(2, "passive mode protocol botch: %s\n", msg);
12979a747e4fSDavid du Colombier 		werrstr("ftp protocol botch");
12989a747e4fSDavid du Colombier 		nopassive = 1;
12999a747e4fSDavid du Colombier 		return Impossible;
13009a747e4fSDavid du Colombier 	}
13019a747e4fSDavid du Colombier 	snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
13029a747e4fSDavid du Colombier 		f[0], f[1], f[2], f[3],
13039a747e4fSDavid du Colombier 		((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
13049a747e4fSDavid du Colombier 
13059a747e4fSDavid du Colombier 	/* open data connection */
13069a747e4fSDavid du Colombier 	fd = dial(ds, 0, 0, 0);
13079a747e4fSDavid du Colombier 	if(fd < 0){
13089a747e4fSDavid du Colombier 		if(debug)
13099a747e4fSDavid du Colombier 			fprint(2, "passive mode connect to %s failed: %r\n", ds);
13109a747e4fSDavid du Colombier 		nopassive = 1;
13119a747e4fSDavid du Colombier 		return TempFail;
13129a747e4fSDavid du Colombier 	}
13139a747e4fSDavid du Colombier 
13149a747e4fSDavid du Colombier 	/* tell remote to send a file */
13159a747e4fSDavid du Colombier 	sendrequest(cmda, cmdb);
13169a747e4fSDavid du Colombier 	x = getreply(&ctlin, msg, sizeof(msg), 0);
13179a747e4fSDavid du Colombier 	if(x != Extra){
13189a747e4fSDavid du Colombier 		close(fd);
13199a747e4fSDavid du Colombier 		if(debug)
13209a747e4fSDavid du Colombier 			fprint(2, "passive mode retrieve failed: %s\n", msg);
13219a747e4fSDavid du Colombier 		werrstr(msg);
13229a747e4fSDavid du Colombier 		return x;
13239a747e4fSDavid du Colombier 	}
13249a747e4fSDavid du Colombier 
1325*1269a55eSDavid du Colombier 	if(usetls){
1326*1269a55eSDavid du Colombier 		memset(&conn, 0, sizeof(conn));
1327*1269a55eSDavid du Colombier 		fd = tlsClient(fd, &conn);
1328*1269a55eSDavid du Colombier 		if(fd < 0)
1329*1269a55eSDavid du Colombier 			fatal("starting tls: %r");
1330*1269a55eSDavid du Colombier 		free(conn.cert);
1331*1269a55eSDavid du Colombier 	}
13329a747e4fSDavid du Colombier 	Binit(&dbuf, fd, mode);
1333*1269a55eSDavid du Colombier 
13349a747e4fSDavid du Colombier 	*bpp = &dbuf;
13359a747e4fSDavid du Colombier 	return Extra;
13369a747e4fSDavid du Colombier }
13379a747e4fSDavid du Colombier 
13389a747e4fSDavid du Colombier static int
13399a747e4fSDavid du Colombier data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
13409a747e4fSDavid du Colombier {
13419a747e4fSDavid du Colombier 	int x;
13429a747e4fSDavid du Colombier 
13439a747e4fSDavid du Colombier 	x = passive(mode, bpp, cmda, cmdb);
13449a747e4fSDavid du Colombier 	if(x != Impossible)
13459a747e4fSDavid du Colombier 		return x;
13469a747e4fSDavid du Colombier 	return active(mode, bpp, cmda, cmdb);
13479a747e4fSDavid du Colombier }
13489a747e4fSDavid du Colombier 
13499a747e4fSDavid du Colombier /*
13509a747e4fSDavid du Colombier  *  used for keep alives
13519a747e4fSDavid du Colombier  */
13529a747e4fSDavid du Colombier void
13539a747e4fSDavid du Colombier nop(void)
13549a747e4fSDavid du Colombier {
13559a747e4fSDavid du Colombier 	if(lastsend - time(0) < 15)
13569a747e4fSDavid du Colombier 		return;
13579a747e4fSDavid du Colombier 	sendrequest("PWD", nil);
13589a747e4fSDavid du Colombier 	getreply(&ctlin, msg, sizeof(msg), 0);
13599a747e4fSDavid du Colombier }
13609a747e4fSDavid du Colombier 
13619a747e4fSDavid du Colombier /*
13629a747e4fSDavid du Colombier  *  turn a vms spec into a path
13639a747e4fSDavid du Colombier  */
13649a747e4fSDavid du Colombier static Node*
13659a747e4fSDavid du Colombier vmsextendpath(Node *np, char *name)
13669a747e4fSDavid du Colombier {
13679a747e4fSDavid du Colombier 	np = extendpath(np, s_copy(name));
13689a747e4fSDavid du Colombier 	if(!ISVALID(np)){
13699a747e4fSDavid du Colombier 		np->d->qid.type = QTDIR;
13709a747e4fSDavid du Colombier 		np->d->atime = time(0);
13719a747e4fSDavid du Colombier 		np->d->mtime = np->d->atime;
13729a747e4fSDavid du Colombier 		strcpy(np->d->uid, "who");
13739a747e4fSDavid du Colombier 		strcpy(np->d->gid, "cares");
13749a747e4fSDavid du Colombier 		np->d->mode = DMDIR|0777;
13759a747e4fSDavid du Colombier 		np->d->length = 0;
13769a747e4fSDavid du Colombier 		if(changedir(np) >= 0)
13779a747e4fSDavid du Colombier 			VALID(np);
13789a747e4fSDavid du Colombier 	}
13799a747e4fSDavid du Colombier 	return np;
13809a747e4fSDavid du Colombier }
13819a747e4fSDavid du Colombier static Node*
13829a747e4fSDavid du Colombier vmsdir(char *name)
13839a747e4fSDavid du Colombier {
13849a747e4fSDavid du Colombier 	char *cp;
13859a747e4fSDavid du Colombier 	Node *np;
13869a747e4fSDavid du Colombier 	char *oname;
13879a747e4fSDavid du Colombier 
13889a747e4fSDavid du Colombier 	np = remroot;
13899a747e4fSDavid du Colombier 	cp = strchr(name, '[');
13909a747e4fSDavid du Colombier 	if(cp)
13919a747e4fSDavid du Colombier 		strcpy(cp, cp+1);
13929a747e4fSDavid du Colombier 	cp = strchr(name, ']');
13939a747e4fSDavid du Colombier 	if(cp)
13949a747e4fSDavid du Colombier 		*cp = 0;
13959a747e4fSDavid du Colombier 	oname = name = strdup(name);
13969a747e4fSDavid du Colombier 	if(name == 0)
13979a747e4fSDavid du Colombier 		return 0;
13989a747e4fSDavid du Colombier 
13999a747e4fSDavid du Colombier 	while(cp = strchr(name, '.')){
14009a747e4fSDavid du Colombier 		*cp = 0;
14019a747e4fSDavid du Colombier 		np = vmsextendpath(np, name);
14029a747e4fSDavid du Colombier 		name = cp+1;
14039a747e4fSDavid du Colombier 	}
14049a747e4fSDavid du Colombier 	np = vmsextendpath(np, name);
14059a747e4fSDavid du Colombier 
14069a747e4fSDavid du Colombier 	/*
14079a747e4fSDavid du Colombier 	 *  walk back to first accessible directory
14089a747e4fSDavid du Colombier 	 */
14099a747e4fSDavid du Colombier 	for(; np->parent != np; np = np->parent)
14109a747e4fSDavid du Colombier 		if(ISVALID(np)){
14119a747e4fSDavid du Colombier 			CACHED(np->parent);
14129a747e4fSDavid du Colombier 			break;
14139a747e4fSDavid du Colombier 		}
14149a747e4fSDavid du Colombier 
14159a747e4fSDavid du Colombier 	free(oname);
14169a747e4fSDavid du Colombier 	return np;
14179a747e4fSDavid du Colombier }
14189a747e4fSDavid du Colombier 
14199a747e4fSDavid du Colombier /*
14209a747e4fSDavid du Colombier  *  walk up the tree building a VMS style path
14219a747e4fSDavid du Colombier  */
14229a747e4fSDavid du Colombier static void
14239a747e4fSDavid du Colombier vmspath(Node *node, String *path)
14249a747e4fSDavid du Colombier {
14259a747e4fSDavid du Colombier 	char *p;
14269a747e4fSDavid du Colombier 	int n;
14279a747e4fSDavid du Colombier 
14289a747e4fSDavid du Colombier 	if(node->depth == 1){
14299a747e4fSDavid du Colombier 		p = strchr(s_to_c(node->remname), ':');
14309a747e4fSDavid du Colombier 		if(p){
14319a747e4fSDavid du Colombier 			n = p - s_to_c(node->remname) + 1;
14329a747e4fSDavid du Colombier 			s_nappend(path, s_to_c(node->remname), n);
14339a747e4fSDavid du Colombier 			s_append(path, "[");
14349a747e4fSDavid du Colombier 			s_append(path, p+1);
14359a747e4fSDavid du Colombier 		} else {
14369a747e4fSDavid du Colombier 			s_append(path, "[");
14379a747e4fSDavid du Colombier 			s_append(path, s_to_c(node->remname));
14389a747e4fSDavid du Colombier 		}
14399a747e4fSDavid du Colombier 		s_append(path, "]");
14409a747e4fSDavid du Colombier 		return;
14419a747e4fSDavid du Colombier 	}
14429a747e4fSDavid du Colombier 	vmspath(node->parent, path);
14439a747e4fSDavid du Colombier 	s_append(path, ".");
14449a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
14459a747e4fSDavid du Colombier }
14469a747e4fSDavid du Colombier 
14479a747e4fSDavid du Colombier /*
14489a747e4fSDavid du Colombier  *  walk up the tree building a Unix style path
14499a747e4fSDavid du Colombier  */
14509a747e4fSDavid du Colombier static void
14519a747e4fSDavid du Colombier unixpath(Node *node, String *path)
14529a747e4fSDavid du Colombier {
14539a747e4fSDavid du Colombier 	if(node == node->parent){
14549a747e4fSDavid du Colombier 		s_append(path, s_to_c(remrootpath));
14559a747e4fSDavid du Colombier 		return;
14569a747e4fSDavid du Colombier 	}
14579a747e4fSDavid du Colombier 	unixpath(node->parent, path);
1458ecc2a59cSDavid du Colombier 	if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
14599a747e4fSDavid du Colombier 		s_append(path, "/");
14609a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
14619a747e4fSDavid du Colombier }
14629a747e4fSDavid du Colombier 
14639a747e4fSDavid du Colombier /*
14649a747e4fSDavid du Colombier  *  walk up the tree building a MVS style path
14659a747e4fSDavid du Colombier  */
14669a747e4fSDavid du Colombier static void
14679a747e4fSDavid du Colombier mvspath(Node *node, String *path)
14689a747e4fSDavid du Colombier {
14699a747e4fSDavid du Colombier 	if(node == node->parent){
14709a747e4fSDavid du Colombier 		s_append(path, s_to_c(remrootpath));
14719a747e4fSDavid du Colombier 		return;
14729a747e4fSDavid du Colombier 	}
14739a747e4fSDavid du Colombier 	mvspath(node->parent, path);
14749a747e4fSDavid du Colombier 	if(s_len(path) > 0)
14759a747e4fSDavid du Colombier 		s_append(path, ".");
14769a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
14779a747e4fSDavid du Colombier }
14789a747e4fSDavid du Colombier 
14799a747e4fSDavid du Colombier static int
14809a747e4fSDavid du Colombier getpassword(char *buf, char *e)
14819a747e4fSDavid du Colombier {
14829a747e4fSDavid du Colombier 	char *p;
14839a747e4fSDavid du Colombier 	int c;
14849a747e4fSDavid du Colombier 	int consctl, rv = 0;
14859a747e4fSDavid du Colombier 
14869a747e4fSDavid du Colombier 	consctl = open("/dev/consctl", OWRITE);
14879a747e4fSDavid du Colombier 	if(consctl >= 0)
14889a747e4fSDavid du Colombier 		write(consctl, "rawon", 5);
14899a747e4fSDavid du Colombier 	print("Password: ");
14909a747e4fSDavid du Colombier 	e--;
14919a747e4fSDavid du Colombier 	for(p = buf; p <= e; p++){
14929a747e4fSDavid du Colombier 		c = Bgetc(&stdin);
14939a747e4fSDavid du Colombier 		if(c < 0){
14949a747e4fSDavid du Colombier 			rv = -1;
14959a747e4fSDavid du Colombier 			goto out;
14969a747e4fSDavid du Colombier 		}
14979a747e4fSDavid du Colombier 		if(c == '\n' || c == '\r')
14989a747e4fSDavid du Colombier 			break;
14999a747e4fSDavid du Colombier 		*p = c;
15009a747e4fSDavid du Colombier 	}
15019a747e4fSDavid du Colombier 	*p = 0;
15029a747e4fSDavid du Colombier 	print("\n");
15039a747e4fSDavid du Colombier 
15049a747e4fSDavid du Colombier out:
15059a747e4fSDavid du Colombier 	if(consctl >= 0)
15069a747e4fSDavid du Colombier 		close(consctl);
15079a747e4fSDavid du Colombier 	return rv;
15089a747e4fSDavid du Colombier }
15099a747e4fSDavid du Colombier 
15109a747e4fSDavid du Colombier /*
15119a747e4fSDavid du Colombier  *  convert from latin1 to utf
15129a747e4fSDavid du Colombier  */
15139a747e4fSDavid du Colombier static char*
15149a747e4fSDavid du Colombier fromlatin1(char *from)
15159a747e4fSDavid du Colombier {
15169a747e4fSDavid du Colombier 	char *p, *to;
15179a747e4fSDavid du Colombier 	Rune r;
15189a747e4fSDavid du Colombier 
15199a747e4fSDavid du Colombier 	if(os == Plan9)
15209a747e4fSDavid du Colombier 		return nil;
15219a747e4fSDavid du Colombier 
15229a747e4fSDavid du Colombier 	/* don't convert if we don't have to */
15239a747e4fSDavid du Colombier 	for(p = from; *p; p++)
15249a747e4fSDavid du Colombier 		if(*p & 0x80)
15259a747e4fSDavid du Colombier 			break;
15269a747e4fSDavid du Colombier 	if(*p == 0)
15279a747e4fSDavid du Colombier 		return nil;
15289a747e4fSDavid du Colombier 
15299a747e4fSDavid du Colombier 	to = malloc(3*strlen(from)+2);
15309a747e4fSDavid du Colombier 	if(to == nil)
15319a747e4fSDavid du Colombier 		return nil;
15329a747e4fSDavid du Colombier 	for(p = to; *from; from++){
15339a747e4fSDavid du Colombier 		r = (*from) & 0xff;
15349a747e4fSDavid du Colombier 		p += runetochar(p, &r);
15359a747e4fSDavid du Colombier 	}
15369a747e4fSDavid du Colombier 	*p = 0;
15379a747e4fSDavid du Colombier 	return to;
15389a747e4fSDavid du Colombier }
15399a747e4fSDavid du Colombier 
15409a747e4fSDavid du Colombier Dir*
15419a747e4fSDavid du Colombier reallocdir(Dir *d, int dofree)
15429a747e4fSDavid du Colombier {
15439a747e4fSDavid du Colombier 	Dir *dp;
15449a747e4fSDavid du Colombier 	char *p;
15459a747e4fSDavid du Colombier 	int nn, ng, nu, nm;
15469a747e4fSDavid du Colombier 	char *utf;
15479a747e4fSDavid du Colombier 
15489a747e4fSDavid du Colombier 	if(d->name == nil)
15499a747e4fSDavid du Colombier 		d->name = "?name?";
15509a747e4fSDavid du Colombier 	if(d->uid == nil)
15519a747e4fSDavid du Colombier 		d->uid = "?uid?";
15529a747e4fSDavid du Colombier 	if(d->gid == nil)
15539a747e4fSDavid du Colombier 		d->gid = d->uid;
15549a747e4fSDavid du Colombier 	if(d->muid == nil)
15559a747e4fSDavid du Colombier 		d->muid = d->uid;
15569a747e4fSDavid du Colombier 
15579a747e4fSDavid du Colombier 	utf = fromlatin1(d->name);
15589a747e4fSDavid du Colombier 	if(utf != nil)
15599a747e4fSDavid du Colombier 		d->name = utf;
15609a747e4fSDavid du Colombier 
15619a747e4fSDavid du Colombier 	nn = strlen(d->name)+1;
15629a747e4fSDavid du Colombier 	nu = strlen(d->uid)+1;
15639a747e4fSDavid du Colombier 	ng = strlen(d->gid)+1;
15649a747e4fSDavid du Colombier 	nm = strlen(d->muid)+1;
15659a747e4fSDavid du Colombier 	dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
15669a747e4fSDavid du Colombier 	if(dp == nil){
15679a747e4fSDavid du Colombier 		if(dofree)
15689a747e4fSDavid du Colombier 			free(d);
15699a747e4fSDavid du Colombier 		if(utf != nil)
15709a747e4fSDavid du Colombier 			free(utf);
15719a747e4fSDavid du Colombier 		return nil;
15729a747e4fSDavid du Colombier 	}
15739a747e4fSDavid du Colombier 	*dp = *d;
15749a747e4fSDavid du Colombier 	p = (char*)&dp[1];
15759a747e4fSDavid du Colombier 	strcpy(p, d->name);
15769a747e4fSDavid du Colombier 	dp->name = p;
15779a747e4fSDavid du Colombier 	p += nn;
15789a747e4fSDavid du Colombier 	strcpy(p, d->uid);
15799a747e4fSDavid du Colombier 	dp->uid = p;
15809a747e4fSDavid du Colombier 	p += nu;
15819a747e4fSDavid du Colombier 	strcpy(p, d->gid);
15829a747e4fSDavid du Colombier 	dp->gid = p;
15839a747e4fSDavid du Colombier 	p += ng;
15849a747e4fSDavid du Colombier 	strcpy(p, d->muid);
15859a747e4fSDavid du Colombier 	dp->muid = p;
15869a747e4fSDavid du Colombier 	if(dofree)
15879a747e4fSDavid du Colombier 		free(d);
15889a747e4fSDavid du Colombier 	if(utf != nil)
15899a747e4fSDavid du Colombier 		free(utf);
15909a747e4fSDavid du Colombier 	return dp;
15919a747e4fSDavid du Colombier }
15929a747e4fSDavid du Colombier 
15939a747e4fSDavid du Colombier Dir*
15949a747e4fSDavid du Colombier dir_change_name(Dir *d, char *name)
15959a747e4fSDavid du Colombier {
15969a747e4fSDavid du Colombier 	if(d->name && strlen(d->name) >= strlen(name)){
15979a747e4fSDavid du Colombier 		strcpy(d->name, name);
15989a747e4fSDavid du Colombier 		return d;
15999a747e4fSDavid du Colombier 	}
16009a747e4fSDavid du Colombier 	d->name = name;
16019a747e4fSDavid du Colombier 	return reallocdir(d, 1);
16029a747e4fSDavid du Colombier }
16039a747e4fSDavid du Colombier 
16049a747e4fSDavid du Colombier Dir*
16059a747e4fSDavid du Colombier dir_change_uid(Dir *d, char *name)
16069a747e4fSDavid du Colombier {
16079a747e4fSDavid du Colombier 	if(d->uid && strlen(d->uid) >= strlen(name)){
16089a747e4fSDavid du Colombier 		strcpy(d->name, name);
16099a747e4fSDavid du Colombier 		return d;
16109a747e4fSDavid du Colombier 	}
16119a747e4fSDavid du Colombier 	d->uid = name;
16129a747e4fSDavid du Colombier 	return reallocdir(d, 1);
16139a747e4fSDavid du Colombier }
16149a747e4fSDavid du Colombier 
16159a747e4fSDavid du Colombier Dir*
16169a747e4fSDavid du Colombier dir_change_gid(Dir *d, char *name)
16179a747e4fSDavid du Colombier {
16189a747e4fSDavid du Colombier 	if(d->gid && strlen(d->gid) >= strlen(name)){
16199a747e4fSDavid du Colombier 		strcpy(d->name, name);
16209a747e4fSDavid du Colombier 		return d;
16219a747e4fSDavid du Colombier 	}
16229a747e4fSDavid du Colombier 	d->gid = name;
16239a747e4fSDavid du Colombier 	return reallocdir(d, 1);
16249a747e4fSDavid du Colombier }
16259a747e4fSDavid du Colombier 
16269a747e4fSDavid du Colombier Dir*
16279a747e4fSDavid du Colombier dir_change_muid(Dir *d, char *name)
16289a747e4fSDavid du Colombier {
16299a747e4fSDavid du Colombier 	if(d->muid && strlen(d->muid) >= strlen(name)){
16309a747e4fSDavid du Colombier 		strcpy(d->name, name);
16319a747e4fSDavid du Colombier 		return d;
16329a747e4fSDavid du Colombier 	}
16339a747e4fSDavid du Colombier 	d->muid = name;
16349a747e4fSDavid du Colombier 	return reallocdir(d, 1);
16359a747e4fSDavid du Colombier }
1636ecc2a59cSDavid du Colombier 
1637ecc2a59cSDavid du Colombier static int
1638ecc2a59cSDavid du Colombier nw_mode(char dirlet, char *s)		/* NetWare file mode mapping */
1639ecc2a59cSDavid du Colombier {
1640ecc2a59cSDavid du Colombier 	int mode = 0777;
1641ecc2a59cSDavid du Colombier 
1642ecc2a59cSDavid du Colombier 	if(dirlet == 'd')
1643ecc2a59cSDavid du Colombier 		mode |= DMDIR;
1644ecc2a59cSDavid du Colombier 
1645ecc2a59cSDavid du Colombier 	if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
1646ecc2a59cSDavid du Colombier 		return(mode);
1647ecc2a59cSDavid du Colombier 
1648ecc2a59cSDavid du Colombier 	if (s[1] == '-')					/* can't read file */
1649ecc2a59cSDavid du Colombier 		mode &= ~0444;
1650ecc2a59cSDavid du Colombier 	if (dirlet == 'd' && s[6] == '-')			/* cannot scan dir */
1651ecc2a59cSDavid du Colombier 		mode &= ~0444;
1652ecc2a59cSDavid du Colombier 	if (s[2] == '-')					/* can't write file */
1653ecc2a59cSDavid du Colombier 		mode &= ~0222;
1654ecc2a59cSDavid du Colombier 	if (dirlet == 'd' && s[7] == '-' && s[3] == '-')	/* cannot create in, or modify dir */
1655ecc2a59cSDavid du Colombier 		mode &= ~0222;
1656ecc2a59cSDavid du Colombier 
1657ecc2a59cSDavid du Colombier 	return(mode);
1658ecc2a59cSDavid du Colombier }
1659