xref: /plan9/sys/src/cmd/hget.c (revision 106486e87debb09341599ec7e9df67e8fc46c504)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
359cc4ca5SDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <bio.h>
559cc4ca5SDavid du Colombier #include <ip.h>
6*106486e8SDavid du Colombier #include <libsec.h>
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier typedef struct URL URL;
97dd7cddfSDavid du Colombier struct URL
107dd7cddfSDavid du Colombier {
117dd7cddfSDavid du Colombier 	int	method;
127dd7cddfSDavid du Colombier 	char	*host;
137dd7cddfSDavid du Colombier 	char	*port;
147dd7cddfSDavid du Colombier 	char	*page;
157dd7cddfSDavid du Colombier 	char	*etag;
167dd7cddfSDavid du Colombier 	char	*redirect;
1759cc4ca5SDavid du Colombier 	char	*postbody;
187dd7cddfSDavid du Colombier 	long	mtime;
197dd7cddfSDavid du Colombier };
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier typedef struct Range Range;
227dd7cddfSDavid du Colombier struct Range
237dd7cddfSDavid du Colombier {
247dd7cddfSDavid du Colombier 	long	start;	/* only 2 gig supported, tdb */
257dd7cddfSDavid du Colombier 	long	end;
267dd7cddfSDavid du Colombier };
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier enum
297dd7cddfSDavid du Colombier {
307dd7cddfSDavid du Colombier 	Http,
31*106486e8SDavid du Colombier 	Https,
327dd7cddfSDavid du Colombier 	Ftp,
337dd7cddfSDavid du Colombier 	Other
347dd7cddfSDavid du Colombier };
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier enum
377dd7cddfSDavid du Colombier {
387dd7cddfSDavid du Colombier 	Eof = 0,
397dd7cddfSDavid du Colombier 	Error = -1,
407dd7cddfSDavid du Colombier 	Server = -2,
417dd7cddfSDavid du Colombier 	Changed = -3,
427dd7cddfSDavid du Colombier };
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier int debug;
457dd7cddfSDavid du Colombier char *ofile;
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier int	doftp(URL*, Range*, int, long);
487dd7cddfSDavid du Colombier int	dohttp(URL*, Range*, int, long);
497dd7cddfSDavid du Colombier int	crackurl(URL*, char*);
507dd7cddfSDavid du Colombier Range*	crackrange(char*);
517dd7cddfSDavid du Colombier int	getheader(int, char*, int);
529a747e4fSDavid du Colombier int	httpheaders(int, int, URL*, Range*);
537dd7cddfSDavid du Colombier int	httprcode(int);
547dd7cddfSDavid du Colombier int	cistrncmp(char*, char*, int);
557dd7cddfSDavid du Colombier int	cistrcmp(char*, char*);
567dd7cddfSDavid du Colombier void	initibuf(void);
577dd7cddfSDavid du Colombier int	readline(int, char*, int);
587dd7cddfSDavid du Colombier int	readibuf(int, char*, int);
597dd7cddfSDavid du Colombier int	dfprint(int, char*, ...);
607dd7cddfSDavid du Colombier void	unreadline(char*);
6159cc4ca5SDavid du Colombier 
627dd7cddfSDavid du Colombier int	verbose;
6359cc4ca5SDavid du Colombier char	*net;
6459cc4ca5SDavid du Colombier char	tcpdir[64];
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier struct {
677dd7cddfSDavid du Colombier 	char	*name;
687dd7cddfSDavid du Colombier 	int	(*f)(URL*, Range*, int, long);
697dd7cddfSDavid du Colombier } method[] = {
707dd7cddfSDavid du Colombier 	[Http]	{ "http",	dohttp },
71*106486e8SDavid du Colombier 	[Https]	{ "https",	dohttp },
727dd7cddfSDavid du Colombier 	[Ftp]	{ "ftp",	doftp },
737dd7cddfSDavid du Colombier 	[Other]	{ "_______",	nil },
747dd7cddfSDavid du Colombier };
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier void
777dd7cddfSDavid du Colombier usage(void)
787dd7cddfSDavid du Colombier {
7959cc4ca5SDavid du Colombier 	fprint(2, "usage: %s [-v] [-o outfile] [-p body] [-x netmtpt] url\n", argv0);
807dd7cddfSDavid du Colombier 	exits("usage");
817dd7cddfSDavid du Colombier }
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier void
847dd7cddfSDavid du Colombier main(int argc, char **argv)
857dd7cddfSDavid du Colombier {
867dd7cddfSDavid du Colombier 	URL u;
877dd7cddfSDavid du Colombier 	Range r;
887dd7cddfSDavid du Colombier 	int fd, errs, n;
899a747e4fSDavid du Colombier 	ulong mtime;
909a747e4fSDavid du Colombier 	Dir *d;
9159cc4ca5SDavid du Colombier 	char postbody[4096], *p, *e, *t;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	ofile = nil;
9459cc4ca5SDavid du Colombier 	p = postbody;
9559cc4ca5SDavid du Colombier 	e = p + sizeof(postbody);
967dd7cddfSDavid du Colombier 	r.start = 0;
977dd7cddfSDavid du Colombier 	r.end = -1;
989a747e4fSDavid du Colombier 	mtime = 0;
9959cc4ca5SDavid du Colombier 	memset(&u, 0, sizeof(u));
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	ARGBEGIN {
1027dd7cddfSDavid du Colombier 	case 'o':
1037dd7cddfSDavid du Colombier 		ofile = ARGF();
1047dd7cddfSDavid du Colombier 		break;
1057dd7cddfSDavid du Colombier 	case 'd':
1067dd7cddfSDavid du Colombier 		debug = 1;
1077dd7cddfSDavid du Colombier 		break;
1087dd7cddfSDavid du Colombier 	case 'v':
1097dd7cddfSDavid du Colombier 		verbose = 1;
1107dd7cddfSDavid du Colombier 		break;
11159cc4ca5SDavid du Colombier 	case 'x':
11259cc4ca5SDavid du Colombier 		net = ARGF();
11359cc4ca5SDavid du Colombier 		if(net == nil)
11459cc4ca5SDavid du Colombier 			usage();
11559cc4ca5SDavid du Colombier 		break;
11659cc4ca5SDavid du Colombier 	case 'p':
11759cc4ca5SDavid du Colombier 		t = ARGF();
11859cc4ca5SDavid du Colombier 		if(t == nil)
11959cc4ca5SDavid du Colombier 			usage();
12059cc4ca5SDavid du Colombier 		if(p != postbody)
12159cc4ca5SDavid du Colombier 			p = seprint(p, e, "&%s", t);
12259cc4ca5SDavid du Colombier 		else
12359cc4ca5SDavid du Colombier 			p = seprint(p, e, "%s", t);
12459cc4ca5SDavid du Colombier 		u.postbody = postbody;
12559cc4ca5SDavid du Colombier 
12659cc4ca5SDavid du Colombier 		break;
1277dd7cddfSDavid du Colombier 	default:
1287dd7cddfSDavid du Colombier 		usage();
1297dd7cddfSDavid du Colombier 	} ARGEND;
1307dd7cddfSDavid du Colombier 
13159cc4ca5SDavid du Colombier 	if(net != nil){
13259cc4ca5SDavid du Colombier 		if(strlen(net) > sizeof(tcpdir)-5)
13359cc4ca5SDavid du Colombier 			sysfatal("network mount point too long");
13459cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net);
13559cc4ca5SDavid du Colombier 	} else
13659cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), "tcp");
13759cc4ca5SDavid du Colombier 
1387dd7cddfSDavid du Colombier 	if(argc != 1)
1397dd7cddfSDavid du Colombier 		usage();
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	fd = 1;
1427dd7cddfSDavid du Colombier 	if(ofile != nil){
1439a747e4fSDavid du Colombier 		d = dirstat(ofile);
1449a747e4fSDavid du Colombier 		if(d == nil){
1457dd7cddfSDavid du Colombier 			fd = create(ofile, OWRITE, 0664);
1467dd7cddfSDavid du Colombier 			if(fd < 0)
1477dd7cddfSDavid du Colombier 				sysfatal("creating %s: %r", ofile);
1487dd7cddfSDavid du Colombier 		} else {
1497dd7cddfSDavid du Colombier 			fd = open(ofile, OWRITE);
1507dd7cddfSDavid du Colombier 			if(fd < 0)
1517dd7cddfSDavid du Colombier 				sysfatal("can't open %s: %r", ofile);
1529a747e4fSDavid du Colombier 			r.start = d->length;
1539a747e4fSDavid du Colombier 			mtime = d->mtime;
1549a747e4fSDavid du Colombier 			free(d);
1557dd7cddfSDavid du Colombier 		}
1567dd7cddfSDavid du Colombier 	}
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	errs = 0;
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier 	if(crackurl(&u, argv[0]) < 0)
1617dd7cddfSDavid du Colombier 		sysfatal("%r");
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	for(;;){
1647dd7cddfSDavid du Colombier 		/* transfer data */
1657dd7cddfSDavid du Colombier 		werrstr("");
16659cc4ca5SDavid du Colombier 		seek(fd, 0, 0);
1679a747e4fSDavid du Colombier 		n = (*method[u.method].f)(&u, &r, fd, mtime);
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 		switch(n){
1707dd7cddfSDavid du Colombier 		case Eof:
1717dd7cddfSDavid du Colombier 			exits(0);
1727dd7cddfSDavid du Colombier 			break;
1737dd7cddfSDavid du Colombier 		case Error:
1747dd7cddfSDavid du Colombier 			if(errs++ < 10)
1757dd7cddfSDavid du Colombier 				continue;
1767dd7cddfSDavid du Colombier 			sysfatal("too many errors with no progress %r");
1777dd7cddfSDavid du Colombier 			break;
1787dd7cddfSDavid du Colombier 		case Server:
1797dd7cddfSDavid du Colombier 			sysfatal("server returned: %r");
1807dd7cddfSDavid du Colombier 			break;
1817dd7cddfSDavid du Colombier 		}
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier 		/* forward progress */
1847dd7cddfSDavid du Colombier 		errs = 0;
1857dd7cddfSDavid du Colombier 		r.start += n;
1867dd7cddfSDavid du Colombier 		if(r.start >= r.end)
1877dd7cddfSDavid du Colombier 			break;
1887dd7cddfSDavid du Colombier 	}
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	exits(0);
1917dd7cddfSDavid du Colombier }
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier int
1947dd7cddfSDavid du Colombier crackurl(URL *u, char *s)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	char *p;
1977dd7cddfSDavid du Colombier 	int i;
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	if(u->host != nil){
2007dd7cddfSDavid du Colombier 		free(u->host);
2017dd7cddfSDavid du Colombier 		u->host = nil;
2027dd7cddfSDavid du Colombier 	}
2037dd7cddfSDavid du Colombier 	if(u->page != nil){
2047dd7cddfSDavid du Colombier 		free(u->page);
2057dd7cddfSDavid du Colombier 		u->page = nil;
2067dd7cddfSDavid du Colombier 	}
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier 	/* get type */
2097dd7cddfSDavid du Colombier 	u->method = Other;
2107dd7cddfSDavid du Colombier 	for(p = s; *p; p++){
2117dd7cddfSDavid du Colombier 		if(*p == '/'){
2127dd7cddfSDavid du Colombier 			u->method = Http;
2137dd7cddfSDavid du Colombier 			p = s;
2147dd7cddfSDavid du Colombier 			break;
2157dd7cddfSDavid du Colombier 		}
2167dd7cddfSDavid du Colombier 		if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){
2177dd7cddfSDavid du Colombier 			*p = 0;
2187dd7cddfSDavid du Colombier 			p += 3;
2197dd7cddfSDavid du Colombier 			for(i = 0; i < nelem(method); i++){
2207dd7cddfSDavid du Colombier 				if(cistrcmp(s, method[i].name) == 0){
2217dd7cddfSDavid du Colombier 					u->method = i;
2227dd7cddfSDavid du Colombier 					break;
2237dd7cddfSDavid du Colombier 				}
2247dd7cddfSDavid du Colombier 			}
2257dd7cddfSDavid du Colombier 			break;
2267dd7cddfSDavid du Colombier 		}
2277dd7cddfSDavid du Colombier 	}
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 	if(u->method == Other){
2307dd7cddfSDavid du Colombier 		werrstr("unsupported URL type %s", s);
2317dd7cddfSDavid du Colombier 		return -1;
2327dd7cddfSDavid du Colombier 	}
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier 	/* get system */
2357dd7cddfSDavid du Colombier 	s = p;
2367dd7cddfSDavid du Colombier 	p = strchr(s, '/');
2377dd7cddfSDavid du Colombier 	if(p == nil){
2387dd7cddfSDavid du Colombier 		u->host = strdup(s);
2397dd7cddfSDavid du Colombier 		u->page = strdup("/");
2407dd7cddfSDavid du Colombier 	} else {
2417dd7cddfSDavid du Colombier 		u->page = strdup(p);
2427dd7cddfSDavid du Colombier 		*p = 0;
2437dd7cddfSDavid du Colombier 		u->host = strdup(s);
2447dd7cddfSDavid du Colombier 		*p = '/';
2457dd7cddfSDavid du Colombier 	}
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	if(p = strchr(u->host, ':')) {
2487dd7cddfSDavid du Colombier 		*p++ = 0;
2497dd7cddfSDavid du Colombier 		u->port = p;
2507dd7cddfSDavid du Colombier 	} else
25159cc4ca5SDavid du Colombier 		u->port = method[u->method].name;
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier 	if(*(u->host) == 0){
2547dd7cddfSDavid du Colombier 		werrstr("bad url, null host");
2557dd7cddfSDavid du Colombier 		return -1;
2567dd7cddfSDavid du Colombier 	}
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	return 0;
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier char *day[] = {
2627dd7cddfSDavid du Colombier 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
2637dd7cddfSDavid du Colombier };
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier char *month[] = {
2667dd7cddfSDavid du Colombier 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2677dd7cddfSDavid du Colombier };
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier struct
2707dd7cddfSDavid du Colombier {
2717dd7cddfSDavid du Colombier 	int	fd;
2727dd7cddfSDavid du Colombier 	long	mtime;
2737dd7cddfSDavid du Colombier } note;
2747dd7cddfSDavid du Colombier 
2757dd7cddfSDavid du Colombier void
2767dd7cddfSDavid du Colombier catch(void*, char*)
2777dd7cddfSDavid du Colombier {
2787dd7cddfSDavid du Colombier 	Dir d;
2797dd7cddfSDavid du Colombier 
2809a747e4fSDavid du Colombier 	nulldir(&d);
2817dd7cddfSDavid du Colombier 	d.mtime = note.mtime;
2827dd7cddfSDavid du Colombier 	if(dirfwstat(note.fd, &d) < 0)
2837dd7cddfSDavid du Colombier 		sysfatal("catch: can't dirfwstat: %r");
2847dd7cddfSDavid du Colombier 	noted(NDFLT);
2857dd7cddfSDavid du Colombier }
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier int
2887dd7cddfSDavid du Colombier dohttp(URL *u, Range *r, int out, long mtime)
2897dd7cddfSDavid du Colombier {
2909a747e4fSDavid du Colombier 	int fd, cfd;
2917dd7cddfSDavid du Colombier 	int redirect, loop;
2927dd7cddfSDavid du Colombier 	int n, rv, code;
2937dd7cddfSDavid du Colombier 	long tot, vtime;
2947dd7cddfSDavid du Colombier 	Tm *tm;
2957dd7cddfSDavid du Colombier 	char buf[1024];
2969a747e4fSDavid du Colombier 	char err[ERRMAX];
2977dd7cddfSDavid du Colombier 
29859cc4ca5SDavid du Colombier 
29959cc4ca5SDavid du Colombier 	/*  always move back to a previous 512 byte bound because some
30059cc4ca5SDavid du Colombier 	 *  servers can't seem to deal with requests that start at the
30159cc4ca5SDavid du Colombier 	 *  end of the file
30259cc4ca5SDavid du Colombier 	 */
30359cc4ca5SDavid du Colombier 	if(r->start)
30459cc4ca5SDavid du Colombier 		r->start = ((r->start-1)/512)*512;
30559cc4ca5SDavid du Colombier 
3067dd7cddfSDavid du Colombier 	/* loop for redirects, requires reading both response code and headers */
3077dd7cddfSDavid du Colombier 	fd = -1;
3087dd7cddfSDavid du Colombier 	for(loop = 0; loop < 32; loop++){
30959cc4ca5SDavid du Colombier 		fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0);
3107dd7cddfSDavid du Colombier 		if(fd < 0)
3117dd7cddfSDavid du Colombier 			return Error;
3127dd7cddfSDavid du Colombier 
313*106486e8SDavid du Colombier 		if(u->method == Https){
314*106486e8SDavid du Colombier 			int tfd;
315*106486e8SDavid du Colombier 			TLSconn conn;
316*106486e8SDavid du Colombier 
317*106486e8SDavid du Colombier 			memset(&conn, 0, sizeof conn);
318*106486e8SDavid du Colombier 			tfd = tlsClient(fd, &conn);
319*106486e8SDavid du Colombier 			if(tfd < 0){
320*106486e8SDavid du Colombier 				fprint(2, "tlsClient: %r\n");
321*106486e8SDavid du Colombier 				close(fd);
322*106486e8SDavid du Colombier 				return Error;
323*106486e8SDavid du Colombier 			}
324*106486e8SDavid du Colombier 			/* BUG: check cert here? */
325*106486e8SDavid du Colombier 			if(conn.cert)
326*106486e8SDavid du Colombier 				free(conn.cert);
327*106486e8SDavid du Colombier 			close(fd);
328*106486e8SDavid du Colombier 			fd = tfd;
329*106486e8SDavid du Colombier 		}
330*106486e8SDavid du Colombier 
3317dd7cddfSDavid du Colombier 		/* write request, use range if not start of file */
33259cc4ca5SDavid du Colombier 		if(u->postbody == nil){
33359cc4ca5SDavid du Colombier 			dfprint(fd,	"GET %s HTTP/1.0\r\n"
33459cc4ca5SDavid du Colombier 					"Host: %s\r\n"
33559cc4ca5SDavid du Colombier 					"User-agent: Plan9/hget\r\n",
33659cc4ca5SDavid du Colombier 					u->page, u->host);
33759cc4ca5SDavid du Colombier 		} else {
33859cc4ca5SDavid du Colombier 			dfprint(fd,	"POST %s HTTP/1.0\r\n"
33959cc4ca5SDavid du Colombier 					"Host: %s\r\n"
34059cc4ca5SDavid du Colombier 					"Content-type: application/x-www-form-urlencoded\r\n"
34159cc4ca5SDavid du Colombier 					"Content-length: %d\r\n"
34259cc4ca5SDavid du Colombier 					"User-agent: Plan9/hget\r\n"
34359cc4ca5SDavid du Colombier 					"\r\n",
34459cc4ca5SDavid du Colombier 					u->page, u->host, strlen(u->postbody));
34559cc4ca5SDavid du Colombier 			dfprint(fd,	"%s", u->postbody);
34659cc4ca5SDavid du Colombier 		}
3477dd7cddfSDavid du Colombier 		if(r->start != 0){
3487dd7cddfSDavid du Colombier 			dfprint(fd, "Range: bytes=%d-\n", r->start);
3497dd7cddfSDavid du Colombier 			if(u->etag != nil){
3507dd7cddfSDavid du Colombier 				dfprint(fd, "If-range: %s\n", u->etag);
3517dd7cddfSDavid du Colombier 			} else {
3527dd7cddfSDavid du Colombier 				tm = gmtime(mtime);
3537dd7cddfSDavid du Colombier 				dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n",
3547dd7cddfSDavid du Colombier 					day[tm->wday], tm->mday, month[tm->mon],
3557dd7cddfSDavid du Colombier 					tm->year+1900, tm->hour, tm->min, tm->sec);
3567dd7cddfSDavid du Colombier 			}
3577dd7cddfSDavid du Colombier 		}
3589a747e4fSDavid du Colombier 		if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
3599a747e4fSDavid du Colombier 			if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){
3609a747e4fSDavid du Colombier 				while((n = read(cfd, buf, sizeof buf)) > 0){
3619a747e4fSDavid du Colombier 					if(debug)
3629a747e4fSDavid du Colombier 						write(2, buf, n);
3639a747e4fSDavid du Colombier 					write(fd, buf, n);
3649a747e4fSDavid du Colombier 				}
3659a747e4fSDavid du Colombier 			}else{
3669a747e4fSDavid du Colombier 				close(cfd);
3679a747e4fSDavid du Colombier 				cfd = -1;
3689a747e4fSDavid du Colombier 			}
3699a747e4fSDavid du Colombier 		}
3707dd7cddfSDavid du Colombier 		dfprint(fd, "\r\n", u->host);
3717dd7cddfSDavid du Colombier 
3727dd7cddfSDavid du Colombier 		redirect = 0;
3737dd7cddfSDavid du Colombier 		initibuf();
3747dd7cddfSDavid du Colombier 		code = httprcode(fd);
3757dd7cddfSDavid du Colombier 		switch(code){
3767dd7cddfSDavid du Colombier 		case Error:	/* connection timed out */
3777dd7cddfSDavid du Colombier 		case Eof:
37859cc4ca5SDavid du Colombier 			close(fd);
3799a747e4fSDavid du Colombier 			close(cfd);
3807dd7cddfSDavid du Colombier 			return code;
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 		case 200:	/* OK */
3837dd7cddfSDavid du Colombier 		case 201:	/* Created */
3847dd7cddfSDavid du Colombier 		case 202:	/* Accepted */
3857dd7cddfSDavid du Colombier 			if(ofile == nil && r->start != 0)
3867dd7cddfSDavid du Colombier 				sysfatal("page changed underfoot");
3877dd7cddfSDavid du Colombier 			break;
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 		case 204:	/* No Content */
3907dd7cddfSDavid du Colombier 			sysfatal("No Content");
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier 		case 206:	/* Partial Content */
3937dd7cddfSDavid du Colombier 			seek(out, r->start, 0);
3947dd7cddfSDavid du Colombier 			break;
3957dd7cddfSDavid du Colombier 
3967dd7cddfSDavid du Colombier 		case 301:	/* Moved Permanently */
3977dd7cddfSDavid du Colombier 		case 302:	/* Moved Temporarily */
3987dd7cddfSDavid du Colombier 			redirect = 1;
3999a747e4fSDavid du Colombier 			u->postbody = nil;
4007dd7cddfSDavid du Colombier 			break;
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier 		case 304:	/* Not Modified */
4037dd7cddfSDavid du Colombier 			break;
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier 		case 400:	/* Bad Request */
4067dd7cddfSDavid du Colombier 			sysfatal("Bad Request");
4077dd7cddfSDavid du Colombier 
4087dd7cddfSDavid du Colombier 		case 401:	/* Unauthorized */
4097dd7cddfSDavid du Colombier 		case 402:	/* ??? */
4107dd7cddfSDavid du Colombier 			sysfatal("Unauthorized");
4117dd7cddfSDavid du Colombier 
4127dd7cddfSDavid du Colombier 		case 403:	/* Forbidden */
4137dd7cddfSDavid du Colombier 			sysfatal("Forbidden by server");
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier 		case 404:	/* Not Found */
4167dd7cddfSDavid du Colombier 			sysfatal("Not found on server");
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier 		case 500:	/* Internal server error */
4197dd7cddfSDavid du Colombier 			sysfatal("Server choked");
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier 		case 501:	/* Not implemented */
4227dd7cddfSDavid du Colombier 			sysfatal("Server can't do it!");
4237dd7cddfSDavid du Colombier 
4247dd7cddfSDavid du Colombier 		case 502:	/* Bad gateway */
4257dd7cddfSDavid du Colombier 			sysfatal("Bad gateway");
4267dd7cddfSDavid du Colombier 
4277dd7cddfSDavid du Colombier 		case 503:	/* Service unavailable */
4287dd7cddfSDavid du Colombier 			sysfatal("Service unavailable");
4297dd7cddfSDavid du Colombier 
4307dd7cddfSDavid du Colombier 		default:
431d9306527SDavid du Colombier 			sysfatal("Unknown response code %d", code);
4327dd7cddfSDavid du Colombier 		}
4337dd7cddfSDavid du Colombier 
4347dd7cddfSDavid du Colombier 		if(u->redirect != nil){
4357dd7cddfSDavid du Colombier 			free(u->redirect);
4367dd7cddfSDavid du Colombier 			u->redirect = nil;
4377dd7cddfSDavid du Colombier 		}
4387dd7cddfSDavid du Colombier 
4399a747e4fSDavid du Colombier 		rv = httpheaders(fd, cfd, u, r);
4409a747e4fSDavid du Colombier 		close(cfd);
44159cc4ca5SDavid du Colombier 		if(rv != 0){
44259cc4ca5SDavid du Colombier 			close(fd);
4437dd7cddfSDavid du Colombier 			return rv;
44459cc4ca5SDavid du Colombier 		}
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 		if(!redirect)
4477dd7cddfSDavid du Colombier 			break;
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 		if(u->redirect == nil)
4507dd7cddfSDavid du Colombier 			sysfatal("redirect: no URL");
4517dd7cddfSDavid du Colombier 		if(crackurl(u, u->redirect) < 0)
4527dd7cddfSDavid du Colombier 			sysfatal("redirect: %r");
4537dd7cddfSDavid du Colombier 	}
4547dd7cddfSDavid du Colombier 
4557dd7cddfSDavid du Colombier 	/* transfer whatever you get */
4567dd7cddfSDavid du Colombier 	if(ofile != nil && u->mtime != 0){
4577dd7cddfSDavid du Colombier 		note.fd = out;
4587dd7cddfSDavid du Colombier 		note.mtime = u->mtime;
4597dd7cddfSDavid du Colombier 		notify(catch);
4607dd7cddfSDavid du Colombier 	}
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	tot = 0;
4637dd7cddfSDavid du Colombier 	vtime = 0;
4647dd7cddfSDavid du Colombier 	for(;;){
4657dd7cddfSDavid du Colombier 		n = readibuf(fd, buf, sizeof(buf));
4667dd7cddfSDavid du Colombier 		if(n <= 0)
4677dd7cddfSDavid du Colombier 			break;
4687dd7cddfSDavid du Colombier 		if(write(out, buf, n) != n)
4697dd7cddfSDavid du Colombier 			break;
4707dd7cddfSDavid du Colombier 		tot += n;
4717dd7cddfSDavid du Colombier 		if(verbose && vtime != time(0)) {
4727dd7cddfSDavid du Colombier 			vtime = time(0);
4737dd7cddfSDavid du Colombier 			fprint(2, "%ld %ld\n", r->start+tot, r->end);
4747dd7cddfSDavid du Colombier 		}
4757dd7cddfSDavid du Colombier 	}
4767dd7cddfSDavid du Colombier 	notify(nil);
47759cc4ca5SDavid du Colombier 	close(fd);
4787dd7cddfSDavid du Colombier 
4797dd7cddfSDavid du Colombier 	if(ofile != nil && u->mtime != 0){
4807dd7cddfSDavid du Colombier 		Dir d;
4817dd7cddfSDavid du Colombier 
4829a747e4fSDavid du Colombier 		rerrstr(err, sizeof err);
4839a747e4fSDavid du Colombier 		nulldir(&d);
4847dd7cddfSDavid du Colombier 		d.mtime = u->mtime;
4857dd7cddfSDavid du Colombier 		if(dirfwstat(out, &d) < 0)
4867dd7cddfSDavid du Colombier 			fprint(2, "couldn't set mtime: %r\n");
4879a747e4fSDavid du Colombier 		errstr(err, sizeof err);
4887dd7cddfSDavid du Colombier 	}
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier 	return tot;
4917dd7cddfSDavid du Colombier }
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier /* get the http response code */
4947dd7cddfSDavid du Colombier int
4957dd7cddfSDavid du Colombier httprcode(int fd)
4967dd7cddfSDavid du Colombier {
4977dd7cddfSDavid du Colombier 	int n;
4987dd7cddfSDavid du Colombier 	char *p;
4997dd7cddfSDavid du Colombier 	char buf[256];
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	n = readline(fd, buf, sizeof(buf)-1);
5027dd7cddfSDavid du Colombier 	if(n <= 0)
5037dd7cddfSDavid du Colombier 		return n;
5047dd7cddfSDavid du Colombier 	if(debug)
5057dd7cddfSDavid du Colombier 		fprint(2, "%d <- %s\n", fd, buf);
5067dd7cddfSDavid du Colombier 	p = strchr(buf, ' ');
5077dd7cddfSDavid du Colombier 	if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){
5087dd7cddfSDavid du Colombier 		werrstr("bad response from server");
5097dd7cddfSDavid du Colombier 		return -1;
5107dd7cddfSDavid du Colombier 	}
5117dd7cddfSDavid du Colombier 	buf[n] = 0;
5127dd7cddfSDavid du Colombier 	return atoi(p+1);
5137dd7cddfSDavid du Colombier }
5147dd7cddfSDavid du Colombier 
5157dd7cddfSDavid du Colombier /* read in and crack the http headers, update u and r */
5167dd7cddfSDavid du Colombier void	hhetag(char*, URL*, Range*);
5177dd7cddfSDavid du Colombier void	hhmtime(char*, URL*, Range*);
5187dd7cddfSDavid du Colombier void	hhclen(char*, URL*, Range*);
5197dd7cddfSDavid du Colombier void	hhcrange(char*, URL*, Range*);
5207dd7cddfSDavid du Colombier void	hhuri(char*, URL*, Range*);
5217dd7cddfSDavid du Colombier void	hhlocation(char*, URL*, Range*);
5227dd7cddfSDavid du Colombier 
5237dd7cddfSDavid du Colombier struct {
5247dd7cddfSDavid du Colombier 	char *name;
5257dd7cddfSDavid du Colombier 	void (*f)(char*, URL*, Range*);
5267dd7cddfSDavid du Colombier } headers[] = {
5277dd7cddfSDavid du Colombier 	{ "etag:", hhetag },
5287dd7cddfSDavid du Colombier 	{ "last-modified:", hhmtime },
5297dd7cddfSDavid du Colombier 	{ "content-length:", hhclen },
5307dd7cddfSDavid du Colombier 	{ "content-range:", hhcrange },
5317dd7cddfSDavid du Colombier 	{ "uri:", hhuri },
5327dd7cddfSDavid du Colombier 	{ "location:", hhlocation },
5337dd7cddfSDavid du Colombier };
5347dd7cddfSDavid du Colombier int
5359a747e4fSDavid du Colombier httpheaders(int fd, int cfd, URL *u, Range *r)
5367dd7cddfSDavid du Colombier {
5377dd7cddfSDavid du Colombier 	char buf[2048];
5387dd7cddfSDavid du Colombier 	char *p;
5397dd7cddfSDavid du Colombier 	int i, n;
5407dd7cddfSDavid du Colombier 
5417dd7cddfSDavid du Colombier 	for(;;){
5427dd7cddfSDavid du Colombier 		n = getheader(fd, buf, sizeof(buf));
5437dd7cddfSDavid du Colombier 		if(n <= 0)
5447dd7cddfSDavid du Colombier 			break;
5459a747e4fSDavid du Colombier 		if(cfd >= 0)
5469a747e4fSDavid du Colombier 			fprint(cfd, "%s\n", buf);
5477dd7cddfSDavid du Colombier 		for(i = 0; i < nelem(headers); i++){
5487dd7cddfSDavid du Colombier 			n = strlen(headers[i].name);
5497dd7cddfSDavid du Colombier 			if(cistrncmp(buf, headers[i].name, n) == 0){
5507dd7cddfSDavid du Colombier 				/* skip field name and leading white */
5517dd7cddfSDavid du Colombier 				p = buf + n;
5527dd7cddfSDavid du Colombier 				while(*p == ' ' || *p == '\t')
5537dd7cddfSDavid du Colombier 					p++;
5547dd7cddfSDavid du Colombier 
5557dd7cddfSDavid du Colombier 				(*headers[i].f)(p, u, r);
5567dd7cddfSDavid du Colombier 				break;
5577dd7cddfSDavid du Colombier 			}
5587dd7cddfSDavid du Colombier 		}
5597dd7cddfSDavid du Colombier 	}
5607dd7cddfSDavid du Colombier 	return n;
5617dd7cddfSDavid du Colombier }
5627dd7cddfSDavid du Colombier 
5637dd7cddfSDavid du Colombier /*
5647dd7cddfSDavid du Colombier  *  read a single mime header, collect continuations.
5657dd7cddfSDavid du Colombier  *
5667dd7cddfSDavid du Colombier  *  this routine assumes that there is a blank line twixt
5677dd7cddfSDavid du Colombier  *  the header and the message body, otherwise bytes will
5687dd7cddfSDavid du Colombier  *  be lost.
5697dd7cddfSDavid du Colombier  */
5707dd7cddfSDavid du Colombier int
5717dd7cddfSDavid du Colombier getheader(int fd, char *buf, int n)
5727dd7cddfSDavid du Colombier {
5737dd7cddfSDavid du Colombier 	char *p, *e;
5747dd7cddfSDavid du Colombier 	int i;
5757dd7cddfSDavid du Colombier 
5767dd7cddfSDavid du Colombier 	n--;
5777dd7cddfSDavid du Colombier 	p = buf;
5787dd7cddfSDavid du Colombier 	for(e = p + n; ; p += i){
5797dd7cddfSDavid du Colombier 		i = readline(fd, p, e-p);
5807dd7cddfSDavid du Colombier 		if(i < 0)
5817dd7cddfSDavid du Colombier 			return i;
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier 		if(p == buf){
5847dd7cddfSDavid du Colombier 			/* first line */
5857dd7cddfSDavid du Colombier 			if(strchr(buf, ':') == nil)
5867dd7cddfSDavid du Colombier 				break;		/* end of headers */
5877dd7cddfSDavid du Colombier 		} else {
5887dd7cddfSDavid du Colombier 			/* continuation line */
5897dd7cddfSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
5907dd7cddfSDavid du Colombier 				unreadline(p);
5917dd7cddfSDavid du Colombier 				*p = 0;
5927dd7cddfSDavid du Colombier 				break;		/* end of this header */
5937dd7cddfSDavid du Colombier 			}
5947dd7cddfSDavid du Colombier 		}
5957dd7cddfSDavid du Colombier 	}
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier 	if(debug)
5987dd7cddfSDavid du Colombier 		fprint(2, "%d <- %s\n", fd, buf);
5997dd7cddfSDavid du Colombier 	return p-buf;
6007dd7cddfSDavid du Colombier }
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier void
6037dd7cddfSDavid du Colombier hhetag(char *p, URL *u, Range*)
6047dd7cddfSDavid du Colombier {
6057dd7cddfSDavid du Colombier 	if(u->etag != nil){
6067dd7cddfSDavid du Colombier 		if(strcmp(u->etag, p) != 0)
6077dd7cddfSDavid du Colombier 			sysfatal("file changed underfoot");
6087dd7cddfSDavid du Colombier 	} else
6097dd7cddfSDavid du Colombier 		u->etag = strdup(p);
6107dd7cddfSDavid du Colombier }
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier char*	monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier void
6157dd7cddfSDavid du Colombier hhmtime(char *p, URL *u, Range*)
6167dd7cddfSDavid du Colombier {
6177dd7cddfSDavid du Colombier 	char *month, *day, *yr, *hms;
6187dd7cddfSDavid du Colombier 	char *fields[6];
6197dd7cddfSDavid du Colombier 	Tm tm, now;
6207dd7cddfSDavid du Colombier 	int i;
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier 	i = getfields(p, fields, 6, 1, " \t");
6237dd7cddfSDavid du Colombier 	if(i < 5)
6247dd7cddfSDavid du Colombier 		return;
6257dd7cddfSDavid du Colombier 
6267dd7cddfSDavid du Colombier 	day = fields[1];
6277dd7cddfSDavid du Colombier 	month = fields[2];
6287dd7cddfSDavid du Colombier 	yr = fields[3];
6297dd7cddfSDavid du Colombier 	hms = fields[4];
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier 	/* default time */
6327dd7cddfSDavid du Colombier 	now = *gmtime(time(0));
6337dd7cddfSDavid du Colombier 	tm = now;
6347dd7cddfSDavid du Colombier 
6357dd7cddfSDavid du Colombier 	/* convert ascii month to a number twixt 1 and 12 */
6367dd7cddfSDavid du Colombier 	if(*month >= '0' && *month <= '9'){
6377dd7cddfSDavid du Colombier 		tm.mon = atoi(month) - 1;
6387dd7cddfSDavid du Colombier 		if(tm.mon < 0 || tm.mon > 11)
6397dd7cddfSDavid du Colombier 			tm.mon = 5;
6407dd7cddfSDavid du Colombier 	} else {
6417dd7cddfSDavid du Colombier 		for(p = month; *p; p++)
6427dd7cddfSDavid du Colombier 			*p = tolower(*p);
6437dd7cddfSDavid du Colombier 		for(i = 0; i < 12; i++)
6447dd7cddfSDavid du Colombier 			if(strncmp(&monthchars[i*3], month, 3) == 0){
6457dd7cddfSDavid du Colombier 				tm.mon = i;
6467dd7cddfSDavid du Colombier 				break;
6477dd7cddfSDavid du Colombier 			}
6487dd7cddfSDavid du Colombier 	}
6497dd7cddfSDavid du Colombier 
6507dd7cddfSDavid du Colombier 	tm.mday = atoi(day);
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier 	if(hms) {
6537dd7cddfSDavid du Colombier 		tm.hour = strtoul(hms, &p, 10);
6547dd7cddfSDavid du Colombier 		if(*p == ':') {
6557dd7cddfSDavid du Colombier 			p++;
6567dd7cddfSDavid du Colombier 			tm.min = strtoul(p, &p, 10);
6577dd7cddfSDavid du Colombier 			if(*p == ':') {
6587dd7cddfSDavid du Colombier 				p++;
6597dd7cddfSDavid du Colombier 				tm.sec = strtoul(p, &p, 10);
6607dd7cddfSDavid du Colombier 			}
6617dd7cddfSDavid du Colombier 		}
6627dd7cddfSDavid du Colombier 		if(tolower(*p) == 'p')
6637dd7cddfSDavid du Colombier 			tm.hour += 12;
6647dd7cddfSDavid du Colombier 	}
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier 	if(yr) {
6677dd7cddfSDavid du Colombier 		tm.year = atoi(yr);
6687dd7cddfSDavid du Colombier 		if(tm.year >= 1900)
6697dd7cddfSDavid du Colombier 			tm.year -= 1900;
6707dd7cddfSDavid du Colombier 	} else {
6717dd7cddfSDavid du Colombier 		if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
6727dd7cddfSDavid du Colombier 			tm.year--;
6737dd7cddfSDavid du Colombier 	}
6747dd7cddfSDavid du Colombier 
6757dd7cddfSDavid du Colombier 	strcpy(tm.zone, "GMT");
6767dd7cddfSDavid du Colombier 	/* convert to epoch seconds */
6777dd7cddfSDavid du Colombier 	u->mtime = tm2sec(&tm);
6787dd7cddfSDavid du Colombier }
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier void
6817dd7cddfSDavid du Colombier hhclen(char *p, URL*, Range *r)
6827dd7cddfSDavid du Colombier {
6837dd7cddfSDavid du Colombier 	r->end = atoi(p);
6847dd7cddfSDavid du Colombier }
6857dd7cddfSDavid du Colombier 
6867dd7cddfSDavid du Colombier void
6877dd7cddfSDavid du Colombier hhcrange(char *p, URL*, Range *r)
6887dd7cddfSDavid du Colombier {
6897dd7cddfSDavid du Colombier 	char *x;
6907dd7cddfSDavid du Colombier 	vlong l;
6917dd7cddfSDavid du Colombier 
6927dd7cddfSDavid du Colombier 	l = 0;
6937dd7cddfSDavid du Colombier 	x = strchr(p, '/');
6947dd7cddfSDavid du Colombier 	if(x)
6957dd7cddfSDavid du Colombier 		l = atoll(x+1);
6967dd7cddfSDavid du Colombier 	if(l == 0)
6977dd7cddfSDavid du Colombier 	x = strchr(p, '-');
6987dd7cddfSDavid du Colombier 	if(x)
6997dd7cddfSDavid du Colombier 		l = atoll(x+1);
7007dd7cddfSDavid du Colombier 	if(l)
7017dd7cddfSDavid du Colombier 		r->end = l;
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier void
7057dd7cddfSDavid du Colombier hhuri(char *p, URL *u, Range*)
7067dd7cddfSDavid du Colombier {
7077dd7cddfSDavid du Colombier 	if(*p != '<')
7087dd7cddfSDavid du Colombier 		return;
7097dd7cddfSDavid du Colombier 	u->redirect = strdup(p+1);
7107dd7cddfSDavid du Colombier 	p = strchr(u->redirect, '>');
7117dd7cddfSDavid du Colombier 	if(p != nil)
7127dd7cddfSDavid du Colombier 		*p = 0;
7137dd7cddfSDavid du Colombier }
7147dd7cddfSDavid du Colombier 
7157dd7cddfSDavid du Colombier void
7167dd7cddfSDavid du Colombier hhlocation(char *p, URL *u, Range*)
7177dd7cddfSDavid du Colombier {
7187dd7cddfSDavid du Colombier 	u->redirect = strdup(p);
7197dd7cddfSDavid du Colombier }
7207dd7cddfSDavid du Colombier 
72159cc4ca5SDavid du Colombier enum
72259cc4ca5SDavid du Colombier {
72359cc4ca5SDavid du Colombier 	/* ftp return codes */
72459cc4ca5SDavid du Colombier 	Extra=		1,
72559cc4ca5SDavid du Colombier 	Success=	2,
72659cc4ca5SDavid du Colombier 	Incomplete=	3,
72759cc4ca5SDavid du Colombier 	TempFail=	4,
72859cc4ca5SDavid du Colombier 	PermFail=	5,
72959cc4ca5SDavid du Colombier 
73059cc4ca5SDavid du Colombier 	Nnetdir=	64,	/* max length of network directory paths */
73159cc4ca5SDavid du Colombier 	Ndialstr=	64,		/* max length of dial strings */
73259cc4ca5SDavid du Colombier };
73359cc4ca5SDavid du Colombier 
73459cc4ca5SDavid du Colombier int ftpcmd(int, char*, ...);
73559cc4ca5SDavid du Colombier int ftprcode(int, char*, int);
73659cc4ca5SDavid du Colombier int hello(int);
73759cc4ca5SDavid du Colombier int logon(int);
73859cc4ca5SDavid du Colombier int xfertype(int, char*);
73959cc4ca5SDavid du Colombier int passive(int, URL*);
74059cc4ca5SDavid du Colombier int active(int, URL*);
74159cc4ca5SDavid du Colombier int ftpxfer(int, int, Range*);
74259cc4ca5SDavid du Colombier int terminateftp(int, int);
74359cc4ca5SDavid du Colombier int getaddrport(char*, uchar*, uchar*);
74459cc4ca5SDavid du Colombier int ftprestart(int, int, URL*, Range*, long);
74559cc4ca5SDavid du Colombier 
74659cc4ca5SDavid du Colombier int
74759cc4ca5SDavid du Colombier doftp(URL *u, Range *r, int out, long mtime)
74859cc4ca5SDavid du Colombier {
7499a747e4fSDavid du Colombier 	int pid, ctl, data, rv;
7509a747e4fSDavid du Colombier 	Waitmsg *w;
75159cc4ca5SDavid du Colombier 	char msg[64];
75259cc4ca5SDavid du Colombier 	char conndir[NETPATHLEN];
75359cc4ca5SDavid du Colombier 	char *p;
75459cc4ca5SDavid du Colombier 
75559cc4ca5SDavid du Colombier 	ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0);
75659cc4ca5SDavid du Colombier 	if(ctl < 0)
75759cc4ca5SDavid du Colombier 		return Error;
75859cc4ca5SDavid du Colombier 	if(net == nil){
75959cc4ca5SDavid du Colombier 		p = strrchr(conndir, '/');
76059cc4ca5SDavid du Colombier 		*p = 0;
76159cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), conndir);
76259cc4ca5SDavid du Colombier 	}
76359cc4ca5SDavid du Colombier 
76459cc4ca5SDavid du Colombier 	initibuf();
76559cc4ca5SDavid du Colombier 
76659cc4ca5SDavid du Colombier 	rv = hello(ctl);
76759cc4ca5SDavid du Colombier 	if(rv < 0)
76859cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
76959cc4ca5SDavid du Colombier 
77059cc4ca5SDavid du Colombier 	rv = logon(ctl);
77159cc4ca5SDavid du Colombier 	if(rv < 0)
77259cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
77359cc4ca5SDavid du Colombier 
77459cc4ca5SDavid du Colombier 	rv = xfertype(ctl, "I");
77559cc4ca5SDavid du Colombier 	if(rv < 0)
77659cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
77759cc4ca5SDavid du Colombier 
77859cc4ca5SDavid du Colombier 	/* if file is up to date and the right size, stop */
77959cc4ca5SDavid du Colombier 	if(ftprestart(ctl, out, u, r, mtime) > 0){
78059cc4ca5SDavid du Colombier 		close(ctl);
78159cc4ca5SDavid du Colombier 		return Eof;
78259cc4ca5SDavid du Colombier 	}
78359cc4ca5SDavid du Colombier 
78459cc4ca5SDavid du Colombier 	/* first try passive mode, then active */
78559cc4ca5SDavid du Colombier 	data = passive(ctl, u);
78659cc4ca5SDavid du Colombier 	if(data < 0){
78759cc4ca5SDavid du Colombier 		data = active(ctl, u);
78859cc4ca5SDavid du Colombier 		if(data < 0)
78959cc4ca5SDavid du Colombier 			return Error;
79059cc4ca5SDavid du Colombier 	}
79159cc4ca5SDavid du Colombier 
79259cc4ca5SDavid du Colombier 	/* fork */
79359cc4ca5SDavid du Colombier 	switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
79459cc4ca5SDavid du Colombier 	case -1:
79559cc4ca5SDavid du Colombier 		close(data);
79659cc4ca5SDavid du Colombier 		return terminateftp(ctl, Error);
79759cc4ca5SDavid du Colombier 	case 0:
79859cc4ca5SDavid du Colombier 		ftpxfer(data, out, r);
79959cc4ca5SDavid du Colombier 		close(data);
80059cc4ca5SDavid du Colombier 		_exits(0);
80159cc4ca5SDavid du Colombier 	default:
80259cc4ca5SDavid du Colombier 		close(data);
80359cc4ca5SDavid du Colombier 		break;
80459cc4ca5SDavid du Colombier 	}
80559cc4ca5SDavid du Colombier 
80659cc4ca5SDavid du Colombier 	/* wait for reply message */
80759cc4ca5SDavid du Colombier 	rv = ftprcode(ctl, msg, sizeof(msg));
80859cc4ca5SDavid du Colombier 	close(ctl);
80959cc4ca5SDavid du Colombier 
81059cc4ca5SDavid du Colombier 	/* wait for process to terminate */
8119a747e4fSDavid du Colombier 	w = nil;
81259cc4ca5SDavid du Colombier 	for(;;){
8139a747e4fSDavid du Colombier 		free(w);
8149a747e4fSDavid du Colombier 		w = wait();
8159a747e4fSDavid du Colombier 		if(w == nil)
81659cc4ca5SDavid du Colombier 			return Error;
8179a747e4fSDavid du Colombier 		if(w->pid == pid){
8189a747e4fSDavid du Colombier 			if(w->msg[0] == 0){
8199a747e4fSDavid du Colombier 				free(w);
82059cc4ca5SDavid du Colombier 				break;
8219a747e4fSDavid du Colombier 			}
8229a747e4fSDavid du Colombier 			werrstr("xfer: %s", w->msg);
8239a747e4fSDavid du Colombier 			free(w);
82459cc4ca5SDavid du Colombier 			return Error;
82559cc4ca5SDavid du Colombier 		}
82659cc4ca5SDavid du Colombier 	}
82759cc4ca5SDavid du Colombier 
82859cc4ca5SDavid du Colombier 	switch(rv){
82959cc4ca5SDavid du Colombier 	case Success:
83059cc4ca5SDavid du Colombier 		return Eof;
83159cc4ca5SDavid du Colombier 	case TempFail:
83259cc4ca5SDavid du Colombier 		return Server;
83359cc4ca5SDavid du Colombier 	default:
83459cc4ca5SDavid du Colombier 		return Error;
83559cc4ca5SDavid du Colombier 	}
83659cc4ca5SDavid du Colombier }
83759cc4ca5SDavid du Colombier 
83859cc4ca5SDavid du Colombier int
83959cc4ca5SDavid du Colombier ftpcmd(int ctl, char *fmt, ...)
84059cc4ca5SDavid du Colombier {
84159cc4ca5SDavid du Colombier 	va_list arg;
84259cc4ca5SDavid du Colombier 	char buf[2*1024], *s;
84359cc4ca5SDavid du Colombier 
84459cc4ca5SDavid du Colombier 	va_start(arg, fmt);
8459a747e4fSDavid du Colombier 	s = vseprint(buf, buf + (sizeof(buf)-4) / sizeof(*buf), fmt, arg);
84659cc4ca5SDavid du Colombier 	va_end(arg);
84759cc4ca5SDavid du Colombier 	if(debug)
84859cc4ca5SDavid du Colombier 		fprint(2, "%d -> %s\n", ctl, buf);
84959cc4ca5SDavid du Colombier 	*s++ = '\r';
85059cc4ca5SDavid du Colombier 	*s++ = '\n';
85159cc4ca5SDavid du Colombier 	if(write(ctl, buf, s - buf) != s - buf)
85259cc4ca5SDavid du Colombier 		return -1;
85359cc4ca5SDavid du Colombier 	return 0;
85459cc4ca5SDavid du Colombier }
85559cc4ca5SDavid du Colombier 
85659cc4ca5SDavid du Colombier int
85759cc4ca5SDavid du Colombier ftprcode(int ctl, char *msg, int len)
85859cc4ca5SDavid du Colombier {
85959cc4ca5SDavid du Colombier 	int rv;
86059cc4ca5SDavid du Colombier 	int i;
861d9306527SDavid du Colombier 	char *p;
86259cc4ca5SDavid du Colombier 
86359cc4ca5SDavid du Colombier 	len--;	/* room for terminating null */
86459cc4ca5SDavid du Colombier 	for(;;){
86559cc4ca5SDavid du Colombier 		*msg = 0;
86659cc4ca5SDavid du Colombier 		i = readline(ctl, msg, len);
86759cc4ca5SDavid du Colombier 		if(i < 0)
86859cc4ca5SDavid du Colombier 			break;
86959cc4ca5SDavid du Colombier 		if(debug)
87059cc4ca5SDavid du Colombier 			fprint(2, "%d <- %s\n", ctl, msg);
87159cc4ca5SDavid du Colombier 
87259cc4ca5SDavid du Colombier 		/* stop if not a continuation */
873d9306527SDavid du Colombier 		rv = strtol(msg, &p, 10);
874d9306527SDavid du Colombier 		if(rv >= 100 && rv < 600 && p==msg+3 && *p == ' ')
87559cc4ca5SDavid du Colombier 			return rv/100;
87659cc4ca5SDavid du Colombier 	}
87759cc4ca5SDavid du Colombier 	*msg = 0;
87859cc4ca5SDavid du Colombier 
87959cc4ca5SDavid du Colombier 	return -1;
88059cc4ca5SDavid du Colombier }
88159cc4ca5SDavid du Colombier 
88259cc4ca5SDavid du Colombier int
88359cc4ca5SDavid du Colombier hello(int ctl)
88459cc4ca5SDavid du Colombier {
88559cc4ca5SDavid du Colombier 	char msg[1024];
88659cc4ca5SDavid du Colombier 
88759cc4ca5SDavid du Colombier 	/* wait for hello from other side */
88859cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
88959cc4ca5SDavid du Colombier 		werrstr("HELLO: %s", msg);
89059cc4ca5SDavid du Colombier 		return Server;
89159cc4ca5SDavid du Colombier 	}
89259cc4ca5SDavid du Colombier 	return 0;
89359cc4ca5SDavid du Colombier }
89459cc4ca5SDavid du Colombier 
89559cc4ca5SDavid du Colombier int
89659cc4ca5SDavid du Colombier getdec(char *p, int n)
89759cc4ca5SDavid du Colombier {
89859cc4ca5SDavid du Colombier 	int x = 0;
89959cc4ca5SDavid du Colombier 	int i;
90059cc4ca5SDavid du Colombier 
90159cc4ca5SDavid du Colombier 	for(i = 0; i < n; i++)
90259cc4ca5SDavid du Colombier 		x = x*10 + (*p++ - '0');
90359cc4ca5SDavid du Colombier 	return x;
90459cc4ca5SDavid du Colombier }
90559cc4ca5SDavid du Colombier 
90659cc4ca5SDavid du Colombier int
90759cc4ca5SDavid du Colombier ftprestart(int ctl, int out, URL *u, Range *r, long mtime)
90859cc4ca5SDavid du Colombier {
90959cc4ca5SDavid du Colombier 	Tm tm;
91059cc4ca5SDavid du Colombier 	char msg[1024];
91159cc4ca5SDavid du Colombier 	long x, rmtime;
91259cc4ca5SDavid du Colombier 
91359cc4ca5SDavid du Colombier 	ftpcmd(ctl, "MDTM %s", u->page);
91459cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
91559cc4ca5SDavid du Colombier 		r->start = 0;
91659cc4ca5SDavid du Colombier 		return 0;		/* need to do something */
91759cc4ca5SDavid du Colombier 	}
91859cc4ca5SDavid du Colombier 
91959cc4ca5SDavid du Colombier 	/* decode modification time */
92059cc4ca5SDavid du Colombier 	if(strlen(msg) < 4 + 4 + 2 + 2 + 2 + 2 + 2){
92159cc4ca5SDavid du Colombier 		r->start = 0;
92259cc4ca5SDavid du Colombier 		return 0;		/* need to do something */
92359cc4ca5SDavid du Colombier 	}
92459cc4ca5SDavid du Colombier 	memset(&tm, 0, sizeof(tm));
92559cc4ca5SDavid du Colombier 	tm.year = getdec(msg+4, 4) - 1900;
92659cc4ca5SDavid du Colombier 	tm.mon = getdec(msg+4+4, 2) - 1;
92759cc4ca5SDavid du Colombier 	tm.mday = getdec(msg+4+4+2, 2);
92859cc4ca5SDavid du Colombier 	tm.hour = getdec(msg+4+4+2+2, 2);
92959cc4ca5SDavid du Colombier 	tm.min = getdec(msg+4+4+2+2+2, 2);
93059cc4ca5SDavid du Colombier 	tm.sec = getdec(msg+4+4+2+2+2+2, 2);
93159cc4ca5SDavid du Colombier 	strcpy(tm.zone, "GMT");
93259cc4ca5SDavid du Colombier 	rmtime = tm2sec(&tm);
93359cc4ca5SDavid du Colombier 	if(rmtime > mtime)
93459cc4ca5SDavid du Colombier 		r->start = 0;
93559cc4ca5SDavid du Colombier 
93659cc4ca5SDavid du Colombier 	/* get size */
93759cc4ca5SDavid du Colombier 	ftpcmd(ctl, "SIZE %s", u->page);
93859cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) == Success){
93959cc4ca5SDavid du Colombier 		x = atol(msg+4);
94059cc4ca5SDavid du Colombier 		if(r->start == x)
94159cc4ca5SDavid du Colombier 			return 1;	/* we're up to date */
94259cc4ca5SDavid du Colombier 		r->end = x;
94359cc4ca5SDavid du Colombier 	}
94459cc4ca5SDavid du Colombier 
94559cc4ca5SDavid du Colombier 	/* seek to restart point */
94659cc4ca5SDavid du Colombier 	if(r->start > 0){
94759cc4ca5SDavid du Colombier 		ftpcmd(ctl, "REST %lud", r->start);
94859cc4ca5SDavid du Colombier 		if(ftprcode(ctl, msg, sizeof(msg)) == Incomplete)
94959cc4ca5SDavid du Colombier 			seek(out, r->start, 0);
95059cc4ca5SDavid du Colombier 		else
95159cc4ca5SDavid du Colombier 			r->start = 0;
95259cc4ca5SDavid du Colombier 	}
95359cc4ca5SDavid du Colombier 
95459cc4ca5SDavid du Colombier 	return 0;	/* need to do something */
95559cc4ca5SDavid du Colombier }
95659cc4ca5SDavid du Colombier 
95759cc4ca5SDavid du Colombier int
95859cc4ca5SDavid du Colombier logon(int ctl)
95959cc4ca5SDavid du Colombier {
96059cc4ca5SDavid du Colombier 	char msg[1024];
96159cc4ca5SDavid du Colombier 
96259cc4ca5SDavid du Colombier 	/* login anonymous */
96359cc4ca5SDavid du Colombier 	ftpcmd(ctl, "USER anonymous");
96459cc4ca5SDavid du Colombier 	switch(ftprcode(ctl, msg, sizeof(msg))){
96559cc4ca5SDavid du Colombier 	case Success:
96659cc4ca5SDavid du Colombier 		return 0;
96759cc4ca5SDavid du Colombier 	case Incomplete:
96859cc4ca5SDavid du Colombier 		break;	/* need password */
96959cc4ca5SDavid du Colombier 	default:
97059cc4ca5SDavid du Colombier 		werrstr("USER: %s", msg);
97159cc4ca5SDavid du Colombier 		return Server;
97259cc4ca5SDavid du Colombier 	}
97359cc4ca5SDavid du Colombier 
97459cc4ca5SDavid du Colombier 	/* send user id as password */
97559cc4ca5SDavid du Colombier 	sprint(msg, "%s@closedmind.org", getuser());
97659cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PASS %s", msg);
97759cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
97859cc4ca5SDavid du Colombier 		werrstr("PASS: %s", msg);
97959cc4ca5SDavid du Colombier 		return Server;
98059cc4ca5SDavid du Colombier 	}
98159cc4ca5SDavid du Colombier 
98259cc4ca5SDavid du Colombier 	return 0;
98359cc4ca5SDavid du Colombier }
98459cc4ca5SDavid du Colombier 
98559cc4ca5SDavid du Colombier int
98659cc4ca5SDavid du Colombier xfertype(int ctl, char *t)
98759cc4ca5SDavid du Colombier {
98859cc4ca5SDavid du Colombier 	char msg[1024];
98959cc4ca5SDavid du Colombier 
99059cc4ca5SDavid du Colombier 	ftpcmd(ctl, "TYPE %s", t);
99159cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
99259cc4ca5SDavid du Colombier 		werrstr("TYPE %s: %s", t, msg);
99359cc4ca5SDavid du Colombier 		return Server;
99459cc4ca5SDavid du Colombier 	}
99559cc4ca5SDavid du Colombier 
99659cc4ca5SDavid du Colombier 	return 0;
99759cc4ca5SDavid du Colombier }
99859cc4ca5SDavid du Colombier 
99959cc4ca5SDavid du Colombier int
100059cc4ca5SDavid du Colombier passive(int ctl, URL *u)
100159cc4ca5SDavid du Colombier {
100259cc4ca5SDavid du Colombier 	char msg[1024];
100359cc4ca5SDavid du Colombier 	char ipaddr[32];
100459cc4ca5SDavid du Colombier 	char *f[6];
100559cc4ca5SDavid du Colombier 	char *p;
100659cc4ca5SDavid du Colombier 	int fd;
100759cc4ca5SDavid du Colombier 	int port;
100859cc4ca5SDavid du Colombier 	char aport[12];
100959cc4ca5SDavid du Colombier 
101059cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PASV");
101159cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success)
101259cc4ca5SDavid du Colombier 		return Error;
101359cc4ca5SDavid du Colombier 
101459cc4ca5SDavid du Colombier 	/* get address and port number from reply, this is AI */
101559cc4ca5SDavid du Colombier 	p = strchr(msg, '(');
101659cc4ca5SDavid du Colombier 	if(p == nil){
101759cc4ca5SDavid du Colombier 		for(p = msg+3; *p; p++)
101859cc4ca5SDavid du Colombier 			if(isdigit(*p))
101959cc4ca5SDavid du Colombier 				break;
102059cc4ca5SDavid du Colombier 	} else
102159cc4ca5SDavid du Colombier 		p++;
102259cc4ca5SDavid du Colombier 	if(getfields(p, f, 6, 0, ",)") < 6){
102359cc4ca5SDavid du Colombier 		werrstr("ftp protocol botch");
102459cc4ca5SDavid du Colombier 		return Server;
102559cc4ca5SDavid du Colombier 	}
102659cc4ca5SDavid du Colombier 	snprint(ipaddr, sizeof(ipaddr), "%s.%s.%s.%s",
102759cc4ca5SDavid du Colombier 		f[0], f[1], f[2], f[3]);
102859cc4ca5SDavid du Colombier 	port = ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff);
102959cc4ca5SDavid du Colombier 	sprint(aport, "%d", port);
103059cc4ca5SDavid du Colombier 
103159cc4ca5SDavid du Colombier 	/* open data connection */
103259cc4ca5SDavid du Colombier 	fd = dial(netmkaddr(ipaddr, tcpdir, aport), 0, 0, 0);
103359cc4ca5SDavid du Colombier 	if(fd < 0){
103459cc4ca5SDavid du Colombier 		werrstr("passive mode failed: %r");
103559cc4ca5SDavid du Colombier 		return Error;
103659cc4ca5SDavid du Colombier 	}
103759cc4ca5SDavid du Colombier 
103859cc4ca5SDavid du Colombier 	/* tell remote to send a file */
103959cc4ca5SDavid du Colombier 	ftpcmd(ctl, "RETR %s", u->page);
104059cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
104159cc4ca5SDavid du Colombier 		werrstr("RETR %s: %s", u->page, msg);
104259cc4ca5SDavid du Colombier 		return Error;
104359cc4ca5SDavid du Colombier 	}
104459cc4ca5SDavid du Colombier 	return fd;
104559cc4ca5SDavid du Colombier }
104659cc4ca5SDavid du Colombier 
104759cc4ca5SDavid du Colombier int
104859cc4ca5SDavid du Colombier active(int ctl, URL *u)
104959cc4ca5SDavid du Colombier {
105059cc4ca5SDavid du Colombier 	char msg[1024];
105159cc4ca5SDavid du Colombier 	char dir[40], ldir[40];
105259cc4ca5SDavid du Colombier 	uchar ipaddr[4];
105359cc4ca5SDavid du Colombier 	uchar port[2];
105459cc4ca5SDavid du Colombier 	int lcfd, dfd, afd;
105559cc4ca5SDavid du Colombier 
105659cc4ca5SDavid du Colombier 	/* announce a port for the call back */
105759cc4ca5SDavid du Colombier 	snprint(msg, sizeof(msg), "%s!*!0", tcpdir);
105859cc4ca5SDavid du Colombier 	afd = announce(msg, dir);
105959cc4ca5SDavid du Colombier 	if(afd < 0)
106059cc4ca5SDavid du Colombier 		return Error;
106159cc4ca5SDavid du Colombier 
106259cc4ca5SDavid du Colombier 	/* get a local address/port of the annoucement */
106359cc4ca5SDavid du Colombier 	if(getaddrport(dir, ipaddr, port) < 0){
106459cc4ca5SDavid du Colombier 		close(afd);
106559cc4ca5SDavid du Colombier 		return Error;
106659cc4ca5SDavid du Colombier 	}
106759cc4ca5SDavid du Colombier 
106859cc4ca5SDavid du Colombier 	/* tell remote side address and port*/
106959cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2],
107059cc4ca5SDavid du Colombier 		ipaddr[3], port[0], port[1]);
107159cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
107259cc4ca5SDavid du Colombier 		close(afd);
107359cc4ca5SDavid du Colombier 		werrstr("active: %s", msg);
107459cc4ca5SDavid du Colombier 		return Error;
107559cc4ca5SDavid du Colombier 	}
107659cc4ca5SDavid du Colombier 
107759cc4ca5SDavid du Colombier 	/* tell remote to send a file */
107859cc4ca5SDavid du Colombier 	ftpcmd(ctl, "RETR %s", u->page);
107959cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
108059cc4ca5SDavid du Colombier 		close(afd);
108159cc4ca5SDavid du Colombier 		werrstr("RETR: %s", msg);
108259cc4ca5SDavid du Colombier 		return Server;
108359cc4ca5SDavid du Colombier 	}
108459cc4ca5SDavid du Colombier 
108559cc4ca5SDavid du Colombier 	/* wait for a connection */
108659cc4ca5SDavid du Colombier 	lcfd = listen(dir, ldir);
108759cc4ca5SDavid du Colombier 	if(lcfd < 0){
108859cc4ca5SDavid du Colombier 		close(afd);
108959cc4ca5SDavid du Colombier 		return Error;
109059cc4ca5SDavid du Colombier 	}
109159cc4ca5SDavid du Colombier 	dfd = accept(lcfd, ldir);
109259cc4ca5SDavid du Colombier 	if(dfd < 0){
109359cc4ca5SDavid du Colombier 		close(afd);
109459cc4ca5SDavid du Colombier 		close(lcfd);
109559cc4ca5SDavid du Colombier 		return Error;
109659cc4ca5SDavid du Colombier 	}
109759cc4ca5SDavid du Colombier 	close(afd);
109859cc4ca5SDavid du Colombier 	close(lcfd);
109959cc4ca5SDavid du Colombier 
110059cc4ca5SDavid du Colombier 	return dfd;
110159cc4ca5SDavid du Colombier }
110259cc4ca5SDavid du Colombier 
110359cc4ca5SDavid du Colombier int
110459cc4ca5SDavid du Colombier ftpxfer(int in, int out, Range *r)
110559cc4ca5SDavid du Colombier {
110659cc4ca5SDavid du Colombier 	char buf[1024];
110759cc4ca5SDavid du Colombier 	long vtime;
110859cc4ca5SDavid du Colombier 	int i, n;
110959cc4ca5SDavid du Colombier 
111059cc4ca5SDavid du Colombier 	vtime = 0;
111159cc4ca5SDavid du Colombier 	for(n = 0;;n += i){
111259cc4ca5SDavid du Colombier 		i = read(in, buf, sizeof(buf));
111359cc4ca5SDavid du Colombier 		if(i == 0)
111459cc4ca5SDavid du Colombier 			break;
111559cc4ca5SDavid du Colombier 		if(i < 0)
111659cc4ca5SDavid du Colombier 			return Error;
111759cc4ca5SDavid du Colombier 		if(write(out, buf, i) != i)
111859cc4ca5SDavid du Colombier 			return Error;
111959cc4ca5SDavid du Colombier 		r->start += i;
112059cc4ca5SDavid du Colombier 		if(verbose && vtime != time(0)) {
112159cc4ca5SDavid du Colombier 			vtime = time(0);
112259cc4ca5SDavid du Colombier 			fprint(2, "%ld %ld\n", r->start, r->end);
112359cc4ca5SDavid du Colombier 		}
112459cc4ca5SDavid du Colombier 	}
112559cc4ca5SDavid du Colombier 	return n;
112659cc4ca5SDavid du Colombier }
112759cc4ca5SDavid du Colombier 
112859cc4ca5SDavid du Colombier int
112959cc4ca5SDavid du Colombier terminateftp(int ctl, int rv)
113059cc4ca5SDavid du Colombier {
113159cc4ca5SDavid du Colombier 	close(ctl);
113259cc4ca5SDavid du Colombier 	return rv;
113359cc4ca5SDavid du Colombier }
113459cc4ca5SDavid du Colombier 
113559cc4ca5SDavid du Colombier /*
113659cc4ca5SDavid du Colombier  * case insensitive strcmp (why aren't these in libc?)
113759cc4ca5SDavid du Colombier  */
11387dd7cddfSDavid du Colombier int
11397dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
11407dd7cddfSDavid du Colombier {
11417dd7cddfSDavid du Colombier 	while(n-- > 0){
11427dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
11437dd7cddfSDavid du Colombier 			return -1;
11447dd7cddfSDavid du Colombier 	}
11457dd7cddfSDavid du Colombier 	return 0;
11467dd7cddfSDavid du Colombier }
11477dd7cddfSDavid du Colombier 
11487dd7cddfSDavid du Colombier int
11497dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
11507dd7cddfSDavid du Colombier {
11517dd7cddfSDavid du Colombier 	while(*a || *b)
11527dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
11537dd7cddfSDavid du Colombier 			return -1;
11547dd7cddfSDavid du Colombier 
11557dd7cddfSDavid du Colombier 	return 0;
11567dd7cddfSDavid du Colombier }
11577dd7cddfSDavid du Colombier 
11587dd7cddfSDavid du Colombier /*
11597dd7cddfSDavid du Colombier  *  buffered io
11607dd7cddfSDavid du Colombier  */
11617dd7cddfSDavid du Colombier struct
11627dd7cddfSDavid du Colombier {
11637dd7cddfSDavid du Colombier 	char *rp;
11647dd7cddfSDavid du Colombier 	char *wp;
11657dd7cddfSDavid du Colombier 	char buf[4*1024];
11667dd7cddfSDavid du Colombier } b;
11677dd7cddfSDavid du Colombier 
11687dd7cddfSDavid du Colombier void
11697dd7cddfSDavid du Colombier initibuf(void)
11707dd7cddfSDavid du Colombier {
11717dd7cddfSDavid du Colombier 	b.rp = b.wp = b.buf;
11727dd7cddfSDavid du Colombier }
11737dd7cddfSDavid du Colombier 
117459cc4ca5SDavid du Colombier /*
117559cc4ca5SDavid du Colombier  *  read a possibly buffered line, strip off trailing while
117659cc4ca5SDavid du Colombier  */
11777dd7cddfSDavid du Colombier int
11787dd7cddfSDavid du Colombier readline(int fd, char *buf, int len)
11797dd7cddfSDavid du Colombier {
11807dd7cddfSDavid du Colombier 	int n;
11817dd7cddfSDavid du Colombier 	char *p;
118259cc4ca5SDavid du Colombier 	int eof = 0;
11837dd7cddfSDavid du Colombier 
11847dd7cddfSDavid du Colombier 	len--;
11857dd7cddfSDavid du Colombier 
11867dd7cddfSDavid du Colombier 	for(p = buf;;){
11877dd7cddfSDavid du Colombier 		if(b.rp >= b.wp){
11887dd7cddfSDavid du Colombier 			n = read(fd, b.wp, sizeof(b.buf)/2);
11897dd7cddfSDavid du Colombier 			if(n < 0)
11907dd7cddfSDavid du Colombier 				return -1;
119159cc4ca5SDavid du Colombier 			if(n == 0){
119259cc4ca5SDavid du Colombier 				eof = 1;
11937dd7cddfSDavid du Colombier 				break;
119459cc4ca5SDavid du Colombier 			}
11957dd7cddfSDavid du Colombier 			b.wp += n;
11967dd7cddfSDavid du Colombier 		}
11977dd7cddfSDavid du Colombier 		n = *b.rp++;
11987dd7cddfSDavid du Colombier 		if(len > 0){
11997dd7cddfSDavid du Colombier 			*p++ = n;
12007dd7cddfSDavid du Colombier 			len--;
12017dd7cddfSDavid du Colombier 		}
12027dd7cddfSDavid du Colombier 		if(n == '\n')
12037dd7cddfSDavid du Colombier 			break;
12047dd7cddfSDavid du Colombier 	}
12057dd7cddfSDavid du Colombier 
12067dd7cddfSDavid du Colombier 	/* drop trailing white */
12077dd7cddfSDavid du Colombier 	for(;;){
12087dd7cddfSDavid du Colombier 		if(p <= buf)
12097dd7cddfSDavid du Colombier 			break;
12107dd7cddfSDavid du Colombier 		n = *(p-1);
12117dd7cddfSDavid du Colombier 		if(n != ' ' && n != '\t' && n != '\r' && n != '\n')
12127dd7cddfSDavid du Colombier 			break;
12137dd7cddfSDavid du Colombier 		p--;
12147dd7cddfSDavid du Colombier 	}
12157dd7cddfSDavid du Colombier 	*p = 0;
121659cc4ca5SDavid du Colombier 
121759cc4ca5SDavid du Colombier 	if(eof && p == buf)
121859cc4ca5SDavid du Colombier 		return -1;
121959cc4ca5SDavid du Colombier 
12207dd7cddfSDavid du Colombier 	return p-buf;
12217dd7cddfSDavid du Colombier }
12227dd7cddfSDavid du Colombier 
12237dd7cddfSDavid du Colombier void
12247dd7cddfSDavid du Colombier unreadline(char *line)
12257dd7cddfSDavid du Colombier {
12267dd7cddfSDavid du Colombier 	int i, n;
12277dd7cddfSDavid du Colombier 
12287dd7cddfSDavid du Colombier 	i = strlen(line);
12297dd7cddfSDavid du Colombier 	n = b.wp-b.rp;
12307dd7cddfSDavid du Colombier 	memmove(&b.buf[i+1], b.rp, n);
12317dd7cddfSDavid du Colombier 	memmove(b.buf, line, i);
12327dd7cddfSDavid du Colombier 	b.buf[i] = '\n';
12337dd7cddfSDavid du Colombier 	b.rp = b.buf;
12347dd7cddfSDavid du Colombier 	b.wp = b.rp + i + 1 + n;
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier 
12377dd7cddfSDavid du Colombier int
12387dd7cddfSDavid du Colombier readibuf(int fd, char *buf, int len)
12397dd7cddfSDavid du Colombier {
12407dd7cddfSDavid du Colombier 	int n;
12417dd7cddfSDavid du Colombier 
12427dd7cddfSDavid du Colombier 	n = b.wp-b.rp;
12437dd7cddfSDavid du Colombier 	if(n > 0){
12447dd7cddfSDavid du Colombier 		if(n > len)
12457dd7cddfSDavid du Colombier 			n = len;
12467dd7cddfSDavid du Colombier 		memmove(buf, b.rp, n);
12477dd7cddfSDavid du Colombier 		b.rp += n;
12487dd7cddfSDavid du Colombier 		return n;
12497dd7cddfSDavid du Colombier 	}
12507dd7cddfSDavid du Colombier 	return read(fd, buf, len);
12517dd7cddfSDavid du Colombier }
12527dd7cddfSDavid du Colombier 
12537dd7cddfSDavid du Colombier int
12547dd7cddfSDavid du Colombier dfprint(int fd, char *fmt, ...)
12557dd7cddfSDavid du Colombier {
12567dd7cddfSDavid du Colombier 	char buf[4*1024];
12577dd7cddfSDavid du Colombier 	va_list arg;
12587dd7cddfSDavid du Colombier 
12597dd7cddfSDavid du Colombier 	va_start(arg, fmt);
12609a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
12617dd7cddfSDavid du Colombier 	va_end(arg);
12627dd7cddfSDavid du Colombier 	if(debug)
12637dd7cddfSDavid du Colombier 		fprint(2, "%d -> %s", fd, buf);
12647dd7cddfSDavid du Colombier 	return fprint(fd, "%s", buf);
12657dd7cddfSDavid du Colombier }
126659cc4ca5SDavid du Colombier 
126759cc4ca5SDavid du Colombier int
126859cc4ca5SDavid du Colombier getaddrport(char *dir, uchar *ipaddr, uchar *port)
126959cc4ca5SDavid du Colombier {
127059cc4ca5SDavid du Colombier 	char buf[256];
127159cc4ca5SDavid du Colombier 	int fd, i;
127259cc4ca5SDavid du Colombier 	char *p;
127359cc4ca5SDavid du Colombier 
127459cc4ca5SDavid du Colombier 	snprint(buf, sizeof(buf), "%s/local", dir);
127559cc4ca5SDavid du Colombier 	fd = open(buf, OREAD);
127659cc4ca5SDavid du Colombier 	if(fd < 0)
127759cc4ca5SDavid du Colombier 		return -1;
127859cc4ca5SDavid du Colombier 	i = read(fd, buf, sizeof(buf)-1);
127959cc4ca5SDavid du Colombier 	close(fd);
128059cc4ca5SDavid du Colombier 	if(i <= 0)
128159cc4ca5SDavid du Colombier 		return -1;
128259cc4ca5SDavid du Colombier 	buf[i] = 0;
128359cc4ca5SDavid du Colombier 	p = strchr(buf, '!');
128459cc4ca5SDavid du Colombier 	if(p != nil)
128559cc4ca5SDavid du Colombier 		*p++ = 0;
128659cc4ca5SDavid du Colombier 	v4parseip(ipaddr, buf);
128759cc4ca5SDavid du Colombier 	i = atoi(p);
128859cc4ca5SDavid du Colombier 	port[0] = i>>8;
128959cc4ca5SDavid du Colombier 	port[1] = i;
129059cc4ca5SDavid du Colombier 	return 0;
129159cc4ca5SDavid du Colombier }
1292