xref: /plan9/sys/src/cmd/hget.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
3*59cc4ca5SDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <bio.h>
5*59cc4ca5SDavid du Colombier #include <ip.h>
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct URL URL;
87dd7cddfSDavid du Colombier struct URL
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	int	method;
117dd7cddfSDavid du Colombier 	char	*host;
127dd7cddfSDavid du Colombier 	char	*port;
137dd7cddfSDavid du Colombier 	char	*page;
147dd7cddfSDavid du Colombier 	char	*etag;
157dd7cddfSDavid du Colombier 	char	*redirect;
16*59cc4ca5SDavid du Colombier 	char	*postbody;
177dd7cddfSDavid du Colombier 	long	mtime;
187dd7cddfSDavid du Colombier };
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier typedef struct Range Range;
217dd7cddfSDavid du Colombier struct Range
227dd7cddfSDavid du Colombier {
237dd7cddfSDavid du Colombier 	long	start;	/* only 2 gig supported, tdb */
247dd7cddfSDavid du Colombier 	long	end;
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier enum
287dd7cddfSDavid du Colombier {
297dd7cddfSDavid du Colombier 	Http,
307dd7cddfSDavid du Colombier 	Ftp,
317dd7cddfSDavid du Colombier 	Other
327dd7cddfSDavid du Colombier };
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier enum
357dd7cddfSDavid du Colombier {
367dd7cddfSDavid du Colombier 	Eof = 0,
377dd7cddfSDavid du Colombier 	Error = -1,
387dd7cddfSDavid du Colombier 	Server = -2,
397dd7cddfSDavid du Colombier 	Changed = -3,
407dd7cddfSDavid du Colombier };
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier int debug;
437dd7cddfSDavid du Colombier char *ofile;
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier int	doftp(URL*, Range*, int, long);
467dd7cddfSDavid du Colombier int	dohttp(URL*, Range*, int, long);
477dd7cddfSDavid du Colombier int	crackurl(URL*, char*);
487dd7cddfSDavid du Colombier Range*	crackrange(char*);
497dd7cddfSDavid du Colombier int	getheader(int, char*, int);
507dd7cddfSDavid du Colombier int	httpheaders(int, URL*, Range*);
517dd7cddfSDavid du Colombier int	httprcode(int);
527dd7cddfSDavid du Colombier int	cistrncmp(char*, char*, int);
537dd7cddfSDavid du Colombier int	cistrcmp(char*, char*);
547dd7cddfSDavid du Colombier void	initibuf(void);
557dd7cddfSDavid du Colombier int	readline(int, char*, int);
567dd7cddfSDavid du Colombier int	readibuf(int, char*, int);
577dd7cddfSDavid du Colombier int	dfprint(int, char*, ...);
587dd7cddfSDavid du Colombier void	unreadline(char*);
59*59cc4ca5SDavid du Colombier 
607dd7cddfSDavid du Colombier int	verbose;
61*59cc4ca5SDavid du Colombier char	*net;
62*59cc4ca5SDavid du Colombier char	tcpdir[64];
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier struct {
657dd7cddfSDavid du Colombier 	char	*name;
667dd7cddfSDavid du Colombier 	int	(*f)(URL*, Range*, int, long);
677dd7cddfSDavid du Colombier } method[] = {
687dd7cddfSDavid du Colombier 	[Http]	{ "http",	dohttp },
697dd7cddfSDavid du Colombier 	[Ftp]	{ "ftp",	doftp },
707dd7cddfSDavid du Colombier 	[Other]	{ "_______",	nil },
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier void
747dd7cddfSDavid du Colombier usage(void)
757dd7cddfSDavid du Colombier {
76*59cc4ca5SDavid du Colombier 	fprint(2, "usage: %s [-v] [-o outfile] [-p body] [-x netmtpt] url\n", argv0);
777dd7cddfSDavid du Colombier 	exits("usage");
787dd7cddfSDavid du Colombier }
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier void
817dd7cddfSDavid du Colombier main(int argc, char **argv)
827dd7cddfSDavid du Colombier {
837dd7cddfSDavid du Colombier 	URL u;
847dd7cddfSDavid du Colombier 	Range r;
857dd7cddfSDavid du Colombier 	int fd, errs, n;
867dd7cddfSDavid du Colombier 	Dir d;
87*59cc4ca5SDavid du Colombier 	char postbody[4096], *p, *e, *t;
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier 	ofile = nil;
90*59cc4ca5SDavid du Colombier 	p = postbody;
91*59cc4ca5SDavid du Colombier 	e = p + sizeof(postbody);
927dd7cddfSDavid du Colombier 	r.start = 0;
937dd7cddfSDavid du Colombier 	r.end = -1;
947dd7cddfSDavid du Colombier 	d.mtime = 0;
95*59cc4ca5SDavid du Colombier 	memset(&u, 0, sizeof(u));
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier 	ARGBEGIN {
987dd7cddfSDavid du Colombier 	case 'o':
997dd7cddfSDavid du Colombier 		ofile = ARGF();
1007dd7cddfSDavid du Colombier 		break;
1017dd7cddfSDavid du Colombier 	case 'd':
1027dd7cddfSDavid du Colombier 		debug = 1;
1037dd7cddfSDavid du Colombier 		break;
1047dd7cddfSDavid du Colombier 	case 'v':
1057dd7cddfSDavid du Colombier 		verbose = 1;
1067dd7cddfSDavid du Colombier 		break;
107*59cc4ca5SDavid du Colombier 	case 'x':
108*59cc4ca5SDavid du Colombier 		net = ARGF();
109*59cc4ca5SDavid du Colombier 		if(net == nil)
110*59cc4ca5SDavid du Colombier 			usage();
111*59cc4ca5SDavid du Colombier 		break;
112*59cc4ca5SDavid du Colombier 	case 'p':
113*59cc4ca5SDavid du Colombier 		t = ARGF();
114*59cc4ca5SDavid du Colombier 		if(t == nil)
115*59cc4ca5SDavid du Colombier 			usage();
116*59cc4ca5SDavid du Colombier 		if(p != postbody)
117*59cc4ca5SDavid du Colombier 			p = seprint(p, e, "&%s", t);
118*59cc4ca5SDavid du Colombier 		else
119*59cc4ca5SDavid du Colombier 			p = seprint(p, e, "%s", t);
120*59cc4ca5SDavid du Colombier 		u.postbody = postbody;
121*59cc4ca5SDavid du Colombier 
122*59cc4ca5SDavid du Colombier 		break;
1237dd7cddfSDavid du Colombier 	default:
1247dd7cddfSDavid du Colombier 		usage();
1257dd7cddfSDavid du Colombier 	} ARGEND;
1267dd7cddfSDavid du Colombier 
127*59cc4ca5SDavid du Colombier 	if(net != nil){
128*59cc4ca5SDavid du Colombier 		if(strlen(net) > sizeof(tcpdir)-5)
129*59cc4ca5SDavid du Colombier 			sysfatal("network mount point too long");
130*59cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net);
131*59cc4ca5SDavid du Colombier 	} else
132*59cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), "tcp");
133*59cc4ca5SDavid du Colombier 
1347dd7cddfSDavid du Colombier 	if(argc != 1)
1357dd7cddfSDavid du Colombier 		usage();
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier 	fd = 1;
1387dd7cddfSDavid du Colombier 	if(ofile != nil){
1397dd7cddfSDavid du Colombier 		if(dirstat(ofile, &d) < 0){
1407dd7cddfSDavid du Colombier 			fd = create(ofile, OWRITE, 0664);
1417dd7cddfSDavid du Colombier 			if(fd < 0)
1427dd7cddfSDavid du Colombier 				sysfatal("creating %s: %r", ofile);
1437dd7cddfSDavid du Colombier 		} else {
1447dd7cddfSDavid du Colombier 			fd = open(ofile, OWRITE);
1457dd7cddfSDavid du Colombier 			if(fd < 0)
1467dd7cddfSDavid du Colombier 				sysfatal("can't open %s: %r", ofile);
1477dd7cddfSDavid du Colombier 			r.start = d.length;
1487dd7cddfSDavid du Colombier 		}
1497dd7cddfSDavid du Colombier 	}
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	errs = 0;
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 	if(crackurl(&u, argv[0]) < 0)
1547dd7cddfSDavid du Colombier 		sysfatal("%r");
1557dd7cddfSDavid du Colombier 
1567dd7cddfSDavid du Colombier 	for(;;){
1577dd7cddfSDavid du Colombier 		/* transfer data */
1587dd7cddfSDavid du Colombier 		werrstr("");
159*59cc4ca5SDavid du Colombier 		seek(fd, 0, 0);
1607dd7cddfSDavid du Colombier 		n = (*method[u.method].f)(&u, &r, fd, d.mtime);
1617dd7cddfSDavid du Colombier 
1627dd7cddfSDavid du Colombier 		switch(n){
1637dd7cddfSDavid du Colombier 		case Eof:
1647dd7cddfSDavid du Colombier 			exits(0);
1657dd7cddfSDavid du Colombier 			break;
1667dd7cddfSDavid du Colombier 		case Error:
1677dd7cddfSDavid du Colombier 			if(errs++ < 10)
1687dd7cddfSDavid du Colombier 				continue;
1697dd7cddfSDavid du Colombier 			sysfatal("too many errors with no progress %r");
1707dd7cddfSDavid du Colombier 			break;
1717dd7cddfSDavid du Colombier 		case Server:
1727dd7cddfSDavid du Colombier 			sysfatal("server returned: %r");
1737dd7cddfSDavid du Colombier 			break;
1747dd7cddfSDavid du Colombier 		}
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier 		/* forward progress */
1777dd7cddfSDavid du Colombier 		errs = 0;
1787dd7cddfSDavid du Colombier 		r.start += n;
1797dd7cddfSDavid du Colombier 		if(r.start >= r.end)
1807dd7cddfSDavid du Colombier 			break;
1817dd7cddfSDavid du Colombier 	}
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier 	exits(0);
1847dd7cddfSDavid du Colombier }
1857dd7cddfSDavid du Colombier 
1867dd7cddfSDavid du Colombier int
1877dd7cddfSDavid du Colombier crackurl(URL *u, char *s)
1887dd7cddfSDavid du Colombier {
1897dd7cddfSDavid du Colombier 	char *p;
1907dd7cddfSDavid du Colombier 	int i;
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 	if(u->host != nil){
1937dd7cddfSDavid du Colombier 		free(u->host);
1947dd7cddfSDavid du Colombier 		u->host = nil;
1957dd7cddfSDavid du Colombier 	}
1967dd7cddfSDavid du Colombier 	if(u->page != nil){
1977dd7cddfSDavid du Colombier 		free(u->page);
1987dd7cddfSDavid du Colombier 		u->page = nil;
1997dd7cddfSDavid du Colombier 	}
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	/* get type */
2027dd7cddfSDavid du Colombier 	u->method = Other;
2037dd7cddfSDavid du Colombier 	for(p = s; *p; p++){
2047dd7cddfSDavid du Colombier 		if(*p == '/'){
2057dd7cddfSDavid du Colombier 			u->method = Http;
2067dd7cddfSDavid du Colombier 			p = s;
2077dd7cddfSDavid du Colombier 			break;
2087dd7cddfSDavid du Colombier 		}
2097dd7cddfSDavid du Colombier 		if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){
2107dd7cddfSDavid du Colombier 			*p = 0;
2117dd7cddfSDavid du Colombier 			p += 3;
2127dd7cddfSDavid du Colombier 			for(i = 0; i < nelem(method); i++){
2137dd7cddfSDavid du Colombier 				if(cistrcmp(s, method[i].name) == 0){
2147dd7cddfSDavid du Colombier 					u->method = i;
2157dd7cddfSDavid du Colombier 					break;
2167dd7cddfSDavid du Colombier 				}
2177dd7cddfSDavid du Colombier 			}
2187dd7cddfSDavid du Colombier 			break;
2197dd7cddfSDavid du Colombier 		}
2207dd7cddfSDavid du Colombier 	}
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 	if(u->method == Other){
2237dd7cddfSDavid du Colombier 		werrstr("unsupported URL type %s", s);
2247dd7cddfSDavid du Colombier 		return -1;
2257dd7cddfSDavid du Colombier 	}
2267dd7cddfSDavid du Colombier 
2277dd7cddfSDavid du Colombier 	/* get system */
2287dd7cddfSDavid du Colombier 	s = p;
2297dd7cddfSDavid du Colombier 	p = strchr(s, '/');
2307dd7cddfSDavid du Colombier 	if(p == nil){
2317dd7cddfSDavid du Colombier 		u->host = strdup(s);
2327dd7cddfSDavid du Colombier 		u->page = strdup("/");
2337dd7cddfSDavid du Colombier 	} else {
2347dd7cddfSDavid du Colombier 		u->page = strdup(p);
2357dd7cddfSDavid du Colombier 		*p = 0;
2367dd7cddfSDavid du Colombier 		u->host = strdup(s);
2377dd7cddfSDavid du Colombier 		*p = '/';
2387dd7cddfSDavid du Colombier 	}
2397dd7cddfSDavid du Colombier 
2407dd7cddfSDavid du Colombier 	if(p = strchr(u->host, ':')) {
2417dd7cddfSDavid du Colombier 		*p++ = 0;
2427dd7cddfSDavid du Colombier 		u->port = p;
2437dd7cddfSDavid du Colombier 	} else
244*59cc4ca5SDavid du Colombier 		u->port = method[u->method].name;
2457dd7cddfSDavid du Colombier 
2467dd7cddfSDavid du Colombier 	if(*(u->host) == 0){
2477dd7cddfSDavid du Colombier 		werrstr("bad url, null host");
2487dd7cddfSDavid du Colombier 		return -1;
2497dd7cddfSDavid du Colombier 	}
2507dd7cddfSDavid du Colombier 
2517dd7cddfSDavid du Colombier 	return 0;
2527dd7cddfSDavid du Colombier }
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier char *day[] = {
2557dd7cddfSDavid du Colombier 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
2567dd7cddfSDavid du Colombier };
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier char *month[] = {
2597dd7cddfSDavid du Colombier 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2607dd7cddfSDavid du Colombier };
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier struct
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier 	int	fd;
2657dd7cddfSDavid du Colombier 	long	mtime;
2667dd7cddfSDavid du Colombier } note;
2677dd7cddfSDavid du Colombier 
2687dd7cddfSDavid du Colombier void
2697dd7cddfSDavid du Colombier catch(void*, char*)
2707dd7cddfSDavid du Colombier {
2717dd7cddfSDavid du Colombier 	Dir d;
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier 	if(dirfstat(note.fd, &d) < 0)
2747dd7cddfSDavid du Colombier 		sysfatal("catch: can't dirfstat: %r");
2757dd7cddfSDavid du Colombier 	d.mtime = note.mtime;
2767dd7cddfSDavid du Colombier 	if(dirfwstat(note.fd, &d) < 0)
2777dd7cddfSDavid du Colombier 		sysfatal("catch: can't dirfwstat: %r");
2787dd7cddfSDavid du Colombier 	noted(NDFLT);
2797dd7cddfSDavid du Colombier }
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier int
2827dd7cddfSDavid du Colombier dohttp(URL *u, Range *r, int out, long mtime)
2837dd7cddfSDavid du Colombier {
2847dd7cddfSDavid du Colombier 	int fd;
2857dd7cddfSDavid du Colombier 	int redirect, loop;
2867dd7cddfSDavid du Colombier 	int n, rv, code;
2877dd7cddfSDavid du Colombier 	long tot, vtime;
2887dd7cddfSDavid du Colombier 	Tm *tm;
2897dd7cddfSDavid du Colombier 	char buf[1024];
2907dd7cddfSDavid du Colombier 	char err[ERRLEN];
2917dd7cddfSDavid du Colombier 
292*59cc4ca5SDavid du Colombier 
293*59cc4ca5SDavid du Colombier 	/*  always move back to a previous 512 byte bound because some
294*59cc4ca5SDavid du Colombier 	 *  servers can't seem to deal with requests that start at the
295*59cc4ca5SDavid du Colombier 	 *  end of the file
296*59cc4ca5SDavid du Colombier 	 */
297*59cc4ca5SDavid du Colombier 	if(r->start)
298*59cc4ca5SDavid du Colombier 		r->start = ((r->start-1)/512)*512;
299*59cc4ca5SDavid du Colombier 
3007dd7cddfSDavid du Colombier 	/* loop for redirects, requires reading both response code and headers */
3017dd7cddfSDavid du Colombier 	fd = -1;
3027dd7cddfSDavid du Colombier 	for(loop = 0; loop < 32; loop++){
303*59cc4ca5SDavid du Colombier 		fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0);
3047dd7cddfSDavid du Colombier 		if(fd < 0)
3057dd7cddfSDavid du Colombier 			return Error;
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier 		/* write request, use range if not start of file */
308*59cc4ca5SDavid du Colombier 		if(u->postbody == nil){
309*59cc4ca5SDavid du Colombier 			dfprint(fd,	"GET %s HTTP/1.0\r\n"
310*59cc4ca5SDavid du Colombier 					"Host: %s\r\n"
311*59cc4ca5SDavid du Colombier 					"User-agent: Plan9/hget\r\n",
312*59cc4ca5SDavid du Colombier 					u->page, u->host);
313*59cc4ca5SDavid du Colombier 		} else {
314*59cc4ca5SDavid du Colombier 			dfprint(fd,	"POST %s HTTP/1.0\r\n"
315*59cc4ca5SDavid du Colombier 					"Host: %s\r\n"
316*59cc4ca5SDavid du Colombier 					"Content-type: application/x-www-form-urlencoded\r\n"
317*59cc4ca5SDavid du Colombier 					"Content-length: %d\r\n"
318*59cc4ca5SDavid du Colombier 					"User-agent: Plan9/hget\r\n"
319*59cc4ca5SDavid du Colombier 					"\r\n",
320*59cc4ca5SDavid du Colombier 					u->page, u->host, strlen(u->postbody));
321*59cc4ca5SDavid du Colombier 			dfprint(fd,	"%s", u->postbody);
322*59cc4ca5SDavid du Colombier 		}
3237dd7cddfSDavid du Colombier 		if(r->start != 0){
3247dd7cddfSDavid du Colombier 			dfprint(fd, "Range: bytes=%d-\n", r->start);
3257dd7cddfSDavid du Colombier 			if(u->etag != nil){
3267dd7cddfSDavid du Colombier 				dfprint(fd, "If-range: %s\n", u->etag);
3277dd7cddfSDavid du Colombier 			} else {
3287dd7cddfSDavid du Colombier 				tm = gmtime(mtime);
3297dd7cddfSDavid du Colombier 				dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n",
3307dd7cddfSDavid du Colombier 					day[tm->wday], tm->mday, month[tm->mon],
3317dd7cddfSDavid du Colombier 					tm->year+1900, tm->hour, tm->min, tm->sec);
3327dd7cddfSDavid du Colombier 			}
3337dd7cddfSDavid du Colombier 		}
3347dd7cddfSDavid du Colombier 		dfprint(fd, "\r\n", u->host);
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 		redirect = 0;
3377dd7cddfSDavid du Colombier 		initibuf();
3387dd7cddfSDavid du Colombier 		code = httprcode(fd);
3397dd7cddfSDavid du Colombier 		switch(code){
3407dd7cddfSDavid du Colombier 		case Error:	/* connection timed out */
3417dd7cddfSDavid du Colombier 		case Eof:
342*59cc4ca5SDavid du Colombier 			close(fd);
3437dd7cddfSDavid du Colombier 			return code;
3447dd7cddfSDavid du Colombier 
3457dd7cddfSDavid du Colombier 		case 200:	/* OK */
3467dd7cddfSDavid du Colombier 		case 201:	/* Created */
3477dd7cddfSDavid du Colombier 		case 202:	/* Accepted */
3487dd7cddfSDavid du Colombier 			if(ofile == nil && r->start != 0)
3497dd7cddfSDavid du Colombier 				sysfatal("page changed underfoot");
3507dd7cddfSDavid du Colombier 			break;
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 		case 204:	/* No Content */
3537dd7cddfSDavid du Colombier 			sysfatal("No Content");
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 		case 206:	/* Partial Content */
3567dd7cddfSDavid du Colombier 			seek(out, r->start, 0);
3577dd7cddfSDavid du Colombier 			break;
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 		case 301:	/* Moved Permanently */
3607dd7cddfSDavid du Colombier 		case 302:	/* Moved Temporarily */
3617dd7cddfSDavid du Colombier 			redirect = 1;
3627dd7cddfSDavid du Colombier 			break;
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier 		case 304:	/* Not Modified */
3657dd7cddfSDavid du Colombier 			break;
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 		case 400:	/* Bad Request */
3687dd7cddfSDavid du Colombier 			sysfatal("Bad Request");
3697dd7cddfSDavid du Colombier 
3707dd7cddfSDavid du Colombier 		case 401:	/* Unauthorized */
3717dd7cddfSDavid du Colombier 		case 402:	/* ??? */
3727dd7cddfSDavid du Colombier 			sysfatal("Unauthorized");
3737dd7cddfSDavid du Colombier 
3747dd7cddfSDavid du Colombier 		case 403:	/* Forbidden */
3757dd7cddfSDavid du Colombier 			sysfatal("Forbidden by server");
3767dd7cddfSDavid du Colombier 
3777dd7cddfSDavid du Colombier 		case 404:	/* Not Found */
3787dd7cddfSDavid du Colombier 			sysfatal("Not found on server");
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 		case 500:	/* Internal server error */
3817dd7cddfSDavid du Colombier 			sysfatal("Server choked");
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier 		case 501:	/* Not implemented */
3847dd7cddfSDavid du Colombier 			sysfatal("Server can't do it!");
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 		case 502:	/* Bad gateway */
3877dd7cddfSDavid du Colombier 			sysfatal("Bad gateway");
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 		case 503:	/* Service unavailable */
3907dd7cddfSDavid du Colombier 			sysfatal("Service unavailable");
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier 		default:
3937dd7cddfSDavid du Colombier 			sysfatal("Unknown response code");
3947dd7cddfSDavid du Colombier 		}
3957dd7cddfSDavid du Colombier 
3967dd7cddfSDavid du Colombier 		if(u->redirect != nil){
3977dd7cddfSDavid du Colombier 			free(u->redirect);
3987dd7cddfSDavid du Colombier 			u->redirect = nil;
3997dd7cddfSDavid du Colombier 		}
4007dd7cddfSDavid du Colombier 
4017dd7cddfSDavid du Colombier 		rv = httpheaders(fd, u, r);
402*59cc4ca5SDavid du Colombier 		if(rv != 0){
403*59cc4ca5SDavid du Colombier 			close(fd);
4047dd7cddfSDavid du Colombier 			return rv;
405*59cc4ca5SDavid du Colombier 		}
4067dd7cddfSDavid du Colombier 
4077dd7cddfSDavid du Colombier 		if(!redirect)
4087dd7cddfSDavid du Colombier 			break;
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 		if(u->redirect == nil)
4117dd7cddfSDavid du Colombier 			sysfatal("redirect: no URL");
4127dd7cddfSDavid du Colombier 		if(crackurl(u, u->redirect) < 0)
4137dd7cddfSDavid du Colombier 			sysfatal("redirect: %r");
4147dd7cddfSDavid du Colombier 	}
4157dd7cddfSDavid du Colombier 
4167dd7cddfSDavid du Colombier 	/* transfer whatever you get */
4177dd7cddfSDavid du Colombier 	if(ofile != nil && u->mtime != 0){
4187dd7cddfSDavid du Colombier 		note.fd = out;
4197dd7cddfSDavid du Colombier 		note.mtime = u->mtime;
4207dd7cddfSDavid du Colombier 		notify(catch);
4217dd7cddfSDavid du Colombier 	}
4227dd7cddfSDavid du Colombier 
4237dd7cddfSDavid du Colombier 	tot = 0;
4247dd7cddfSDavid du Colombier 	vtime = 0;
4257dd7cddfSDavid du Colombier 	for(;;){
4267dd7cddfSDavid du Colombier 		n = readibuf(fd, buf, sizeof(buf));
4277dd7cddfSDavid du Colombier 		if(n <= 0)
4287dd7cddfSDavid du Colombier 			break;
4297dd7cddfSDavid du Colombier 		if(write(out, buf, n) != n)
4307dd7cddfSDavid du Colombier 			break;
4317dd7cddfSDavid du Colombier 		tot += n;
4327dd7cddfSDavid du Colombier 		if(verbose && vtime != time(0)) {
4337dd7cddfSDavid du Colombier 			vtime = time(0);
4347dd7cddfSDavid du Colombier 			fprint(2, "%ld %ld\n", r->start+tot, r->end);
4357dd7cddfSDavid du Colombier 		}
4367dd7cddfSDavid du Colombier 	}
4377dd7cddfSDavid du Colombier 	notify(nil);
438*59cc4ca5SDavid du Colombier 	close(fd);
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 	errstr(err);
4417dd7cddfSDavid du Colombier 	if(ofile != nil && u->mtime != 0){
4427dd7cddfSDavid du Colombier 		Dir d;
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier 		dirfstat(out, &d);
4457dd7cddfSDavid du Colombier 		d.mtime = u->mtime;
4467dd7cddfSDavid du Colombier 		if(dirfwstat(out, &d) < 0)
4477dd7cddfSDavid du Colombier 			fprint(2, "couldn't set mtime: %r\n");
4487dd7cddfSDavid du Colombier 	}
4497dd7cddfSDavid du Colombier 	errstr(err);
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	return tot;
4527dd7cddfSDavid du Colombier }
4537dd7cddfSDavid du Colombier 
4547dd7cddfSDavid du Colombier /* get the http response code */
4557dd7cddfSDavid du Colombier int
4567dd7cddfSDavid du Colombier httprcode(int fd)
4577dd7cddfSDavid du Colombier {
4587dd7cddfSDavid du Colombier 	int n;
4597dd7cddfSDavid du Colombier 	char *p;
4607dd7cddfSDavid du Colombier 	char buf[256];
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	n = readline(fd, buf, sizeof(buf)-1);
4637dd7cddfSDavid du Colombier 	if(n <= 0)
4647dd7cddfSDavid du Colombier 		return n;
4657dd7cddfSDavid du Colombier 	if(debug)
4667dd7cddfSDavid du Colombier 		fprint(2, "%d <- %s\n", fd, buf);
4677dd7cddfSDavid du Colombier 	p = strchr(buf, ' ');
4687dd7cddfSDavid du Colombier 	if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){
4697dd7cddfSDavid du Colombier 		werrstr("bad response from server");
4707dd7cddfSDavid du Colombier 		return -1;
4717dd7cddfSDavid du Colombier 	}
4727dd7cddfSDavid du Colombier 	buf[n] = 0;
4737dd7cddfSDavid du Colombier 	return atoi(p+1);
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier /* read in and crack the http headers, update u and r */
4777dd7cddfSDavid du Colombier void	hhetag(char*, URL*, Range*);
4787dd7cddfSDavid du Colombier void	hhmtime(char*, URL*, Range*);
4797dd7cddfSDavid du Colombier void	hhclen(char*, URL*, Range*);
4807dd7cddfSDavid du Colombier void	hhcrange(char*, URL*, Range*);
4817dd7cddfSDavid du Colombier void	hhuri(char*, URL*, Range*);
4827dd7cddfSDavid du Colombier void	hhlocation(char*, URL*, Range*);
4837dd7cddfSDavid du Colombier 
4847dd7cddfSDavid du Colombier struct {
4857dd7cddfSDavid du Colombier 	char *name;
4867dd7cddfSDavid du Colombier 	void (*f)(char*, URL*, Range*);
4877dd7cddfSDavid du Colombier } headers[] = {
4887dd7cddfSDavid du Colombier 	{ "etag:", hhetag },
4897dd7cddfSDavid du Colombier 	{ "last-modified:", hhmtime },
4907dd7cddfSDavid du Colombier 	{ "content-length:", hhclen },
4917dd7cddfSDavid du Colombier 	{ "content-range:", hhcrange },
4927dd7cddfSDavid du Colombier 	{ "uri:", hhuri },
4937dd7cddfSDavid du Colombier 	{ "location:", hhlocation },
4947dd7cddfSDavid du Colombier };
4957dd7cddfSDavid du Colombier int
4967dd7cddfSDavid du Colombier httpheaders(int fd, URL *u, Range *r)
4977dd7cddfSDavid du Colombier {
4987dd7cddfSDavid du Colombier 	char buf[2048];
4997dd7cddfSDavid du Colombier 	char *p;
5007dd7cddfSDavid du Colombier 	int i, n;
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier 	for(;;){
5037dd7cddfSDavid du Colombier 		n = getheader(fd, buf, sizeof(buf));
5047dd7cddfSDavid du Colombier 		if(n <= 0)
5057dd7cddfSDavid du Colombier 			break;
5067dd7cddfSDavid du Colombier 		for(i = 0; i < nelem(headers); i++){
5077dd7cddfSDavid du Colombier 			n = strlen(headers[i].name);
5087dd7cddfSDavid du Colombier 			if(cistrncmp(buf, headers[i].name, n) == 0){
5097dd7cddfSDavid du Colombier 				/* skip field name and leading white */
5107dd7cddfSDavid du Colombier 				p = buf + n;
5117dd7cddfSDavid du Colombier 				while(*p == ' ' || *p == '\t')
5127dd7cddfSDavid du Colombier 					p++;
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier 				(*headers[i].f)(p, u, r);
5157dd7cddfSDavid du Colombier 				break;
5167dd7cddfSDavid du Colombier 			}
5177dd7cddfSDavid du Colombier 		}
5187dd7cddfSDavid du Colombier 	}
5197dd7cddfSDavid du Colombier 	return n;
5207dd7cddfSDavid du Colombier }
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier /*
5237dd7cddfSDavid du Colombier  *  read a single mime header, collect continuations.
5247dd7cddfSDavid du Colombier  *
5257dd7cddfSDavid du Colombier  *  this routine assumes that there is a blank line twixt
5267dd7cddfSDavid du Colombier  *  the header and the message body, otherwise bytes will
5277dd7cddfSDavid du Colombier  *  be lost.
5287dd7cddfSDavid du Colombier  */
5297dd7cddfSDavid du Colombier int
5307dd7cddfSDavid du Colombier getheader(int fd, char *buf, int n)
5317dd7cddfSDavid du Colombier {
5327dd7cddfSDavid du Colombier 	char *p, *e;
5337dd7cddfSDavid du Colombier 	int i;
5347dd7cddfSDavid du Colombier 
5357dd7cddfSDavid du Colombier 	n--;
5367dd7cddfSDavid du Colombier 	p = buf;
5377dd7cddfSDavid du Colombier 	for(e = p + n; ; p += i){
5387dd7cddfSDavid du Colombier 		i = readline(fd, p, e-p);
5397dd7cddfSDavid du Colombier 		if(i < 0)
5407dd7cddfSDavid du Colombier 			return i;
5417dd7cddfSDavid du Colombier 
5427dd7cddfSDavid du Colombier 		if(p == buf){
5437dd7cddfSDavid du Colombier 			/* first line */
5447dd7cddfSDavid du Colombier 			if(strchr(buf, ':') == nil)
5457dd7cddfSDavid du Colombier 				break;		/* end of headers */
5467dd7cddfSDavid du Colombier 		} else {
5477dd7cddfSDavid du Colombier 			/* continuation line */
5487dd7cddfSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
5497dd7cddfSDavid du Colombier 				unreadline(p);
5507dd7cddfSDavid du Colombier 				*p = 0;
5517dd7cddfSDavid du Colombier 				break;		/* end of this header */
5527dd7cddfSDavid du Colombier 			}
5537dd7cddfSDavid du Colombier 		}
5547dd7cddfSDavid du Colombier 	}
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier 	if(debug)
5577dd7cddfSDavid du Colombier 		fprint(2, "%d <- %s\n", fd, buf);
5587dd7cddfSDavid du Colombier 	return p-buf;
5597dd7cddfSDavid du Colombier }
5607dd7cddfSDavid du Colombier 
5617dd7cddfSDavid du Colombier void
5627dd7cddfSDavid du Colombier hhetag(char *p, URL *u, Range*)
5637dd7cddfSDavid du Colombier {
5647dd7cddfSDavid du Colombier 	if(u->etag != nil){
5657dd7cddfSDavid du Colombier 		if(strcmp(u->etag, p) != 0)
5667dd7cddfSDavid du Colombier 			sysfatal("file changed underfoot");
5677dd7cddfSDavid du Colombier 	} else
5687dd7cddfSDavid du Colombier 		u->etag = strdup(p);
5697dd7cddfSDavid du Colombier }
5707dd7cddfSDavid du Colombier 
5717dd7cddfSDavid du Colombier char*	monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
5727dd7cddfSDavid du Colombier 
5737dd7cddfSDavid du Colombier void
5747dd7cddfSDavid du Colombier hhmtime(char *p, URL *u, Range*)
5757dd7cddfSDavid du Colombier {
5767dd7cddfSDavid du Colombier 	char *month, *day, *yr, *hms;
5777dd7cddfSDavid du Colombier 	char *fields[6];
5787dd7cddfSDavid du Colombier 	Tm tm, now;
5797dd7cddfSDavid du Colombier 	int i;
5807dd7cddfSDavid du Colombier 
5817dd7cddfSDavid du Colombier 	i = getfields(p, fields, 6, 1, " \t");
5827dd7cddfSDavid du Colombier 	if(i < 5)
5837dd7cddfSDavid du Colombier 		return;
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier 	day = fields[1];
5867dd7cddfSDavid du Colombier 	month = fields[2];
5877dd7cddfSDavid du Colombier 	yr = fields[3];
5887dd7cddfSDavid du Colombier 	hms = fields[4];
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier 	/* default time */
5917dd7cddfSDavid du Colombier 	now = *gmtime(time(0));
5927dd7cddfSDavid du Colombier 	tm = now;
5937dd7cddfSDavid du Colombier 
5947dd7cddfSDavid du Colombier 	/* convert ascii month to a number twixt 1 and 12 */
5957dd7cddfSDavid du Colombier 	if(*month >= '0' && *month <= '9'){
5967dd7cddfSDavid du Colombier 		tm.mon = atoi(month) - 1;
5977dd7cddfSDavid du Colombier 		if(tm.mon < 0 || tm.mon > 11)
5987dd7cddfSDavid du Colombier 			tm.mon = 5;
5997dd7cddfSDavid du Colombier 	} else {
6007dd7cddfSDavid du Colombier 		for(p = month; *p; p++)
6017dd7cddfSDavid du Colombier 			*p = tolower(*p);
6027dd7cddfSDavid du Colombier 		for(i = 0; i < 12; i++)
6037dd7cddfSDavid du Colombier 			if(strncmp(&monthchars[i*3], month, 3) == 0){
6047dd7cddfSDavid du Colombier 				tm.mon = i;
6057dd7cddfSDavid du Colombier 				break;
6067dd7cddfSDavid du Colombier 			}
6077dd7cddfSDavid du Colombier 	}
6087dd7cddfSDavid du Colombier 
6097dd7cddfSDavid du Colombier 	tm.mday = atoi(day);
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 	if(hms) {
6127dd7cddfSDavid du Colombier 		tm.hour = strtoul(hms, &p, 10);
6137dd7cddfSDavid du Colombier 		if(*p == ':') {
6147dd7cddfSDavid du Colombier 			p++;
6157dd7cddfSDavid du Colombier 			tm.min = strtoul(p, &p, 10);
6167dd7cddfSDavid du Colombier 			if(*p == ':') {
6177dd7cddfSDavid du Colombier 				p++;
6187dd7cddfSDavid du Colombier 				tm.sec = strtoul(p, &p, 10);
6197dd7cddfSDavid du Colombier 			}
6207dd7cddfSDavid du Colombier 		}
6217dd7cddfSDavid du Colombier 		if(tolower(*p) == 'p')
6227dd7cddfSDavid du Colombier 			tm.hour += 12;
6237dd7cddfSDavid du Colombier 	}
6247dd7cddfSDavid du Colombier 
6257dd7cddfSDavid du Colombier 	if(yr) {
6267dd7cddfSDavid du Colombier 		tm.year = atoi(yr);
6277dd7cddfSDavid du Colombier 		if(tm.year >= 1900)
6287dd7cddfSDavid du Colombier 			tm.year -= 1900;
6297dd7cddfSDavid du Colombier 	} else {
6307dd7cddfSDavid du Colombier 		if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
6317dd7cddfSDavid du Colombier 			tm.year--;
6327dd7cddfSDavid du Colombier 	}
6337dd7cddfSDavid du Colombier 
6347dd7cddfSDavid du Colombier 	strcpy(tm.zone, "GMT");
6357dd7cddfSDavid du Colombier 	/* convert to epoch seconds */
6367dd7cddfSDavid du Colombier 	u->mtime = tm2sec(&tm);
6377dd7cddfSDavid du Colombier }
6387dd7cddfSDavid du Colombier 
6397dd7cddfSDavid du Colombier void
6407dd7cddfSDavid du Colombier hhclen(char *p, URL*, Range *r)
6417dd7cddfSDavid du Colombier {
6427dd7cddfSDavid du Colombier 	r->end = atoi(p);
6437dd7cddfSDavid du Colombier }
6447dd7cddfSDavid du Colombier 
6457dd7cddfSDavid du Colombier void
6467dd7cddfSDavid du Colombier hhcrange(char *p, URL*, Range *r)
6477dd7cddfSDavid du Colombier {
6487dd7cddfSDavid du Colombier 	char *x;
6497dd7cddfSDavid du Colombier 	vlong l;
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier 	l = 0;
6527dd7cddfSDavid du Colombier 	x = strchr(p, '/');
6537dd7cddfSDavid du Colombier 	if(x)
6547dd7cddfSDavid du Colombier 		l = atoll(x+1);
6557dd7cddfSDavid du Colombier 	if(l == 0)
6567dd7cddfSDavid du Colombier 	x = strchr(p, '-');
6577dd7cddfSDavid du Colombier 	if(x)
6587dd7cddfSDavid du Colombier 		l = atoll(x+1);
6597dd7cddfSDavid du Colombier 	if(l)
6607dd7cddfSDavid du Colombier 		r->end = l;
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier void
6647dd7cddfSDavid du Colombier hhuri(char *p, URL *u, Range*)
6657dd7cddfSDavid du Colombier {
6667dd7cddfSDavid du Colombier 	if(*p != '<')
6677dd7cddfSDavid du Colombier 		return;
6687dd7cddfSDavid du Colombier 	u->redirect = strdup(p+1);
6697dd7cddfSDavid du Colombier 	p = strchr(u->redirect, '>');
6707dd7cddfSDavid du Colombier 	if(p != nil)
6717dd7cddfSDavid du Colombier 		*p = 0;
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier void
6757dd7cddfSDavid du Colombier hhlocation(char *p, URL *u, Range*)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier 	u->redirect = strdup(p);
6787dd7cddfSDavid du Colombier }
6797dd7cddfSDavid du Colombier 
680*59cc4ca5SDavid du Colombier enum
681*59cc4ca5SDavid du Colombier {
682*59cc4ca5SDavid du Colombier 	/* ftp return codes */
683*59cc4ca5SDavid du Colombier 	Extra=		1,
684*59cc4ca5SDavid du Colombier 	Success=	2,
685*59cc4ca5SDavid du Colombier 	Incomplete=	3,
686*59cc4ca5SDavid du Colombier 	TempFail=	4,
687*59cc4ca5SDavid du Colombier 	PermFail=	5,
688*59cc4ca5SDavid du Colombier 
689*59cc4ca5SDavid du Colombier 	Nnetdir=	64,	/* max length of network directory paths */
690*59cc4ca5SDavid du Colombier 	Ndialstr=	64,		/* max length of dial strings */
691*59cc4ca5SDavid du Colombier };
692*59cc4ca5SDavid du Colombier 
693*59cc4ca5SDavid du Colombier int ftpcmd(int, char*, ...);
694*59cc4ca5SDavid du Colombier int ftprcode(int, char*, int);
695*59cc4ca5SDavid du Colombier int hello(int);
696*59cc4ca5SDavid du Colombier int logon(int);
697*59cc4ca5SDavid du Colombier int xfertype(int, char*);
698*59cc4ca5SDavid du Colombier int passive(int, URL*);
699*59cc4ca5SDavid du Colombier int active(int, URL*);
700*59cc4ca5SDavid du Colombier int ftpxfer(int, int, Range*);
701*59cc4ca5SDavid du Colombier int terminateftp(int, int);
702*59cc4ca5SDavid du Colombier int getaddrport(char*, uchar*, uchar*);
703*59cc4ca5SDavid du Colombier int ftprestart(int, int, URL*, Range*, long);
704*59cc4ca5SDavid du Colombier 
705*59cc4ca5SDavid du Colombier int
706*59cc4ca5SDavid du Colombier doftp(URL *u, Range *r, int out, long mtime)
707*59cc4ca5SDavid du Colombier {
708*59cc4ca5SDavid du Colombier 	int pid, ctl, data, rv, wrv;
709*59cc4ca5SDavid du Colombier 	Waitmsg w;
710*59cc4ca5SDavid du Colombier 	char msg[64];
711*59cc4ca5SDavid du Colombier 	char conndir[NETPATHLEN];
712*59cc4ca5SDavid du Colombier 	char *p;
713*59cc4ca5SDavid du Colombier 
714*59cc4ca5SDavid du Colombier 	ctl = dial(netmkaddr(u->host, tcpdir, u->port), 0, conndir, 0);
715*59cc4ca5SDavid du Colombier 	if(ctl < 0)
716*59cc4ca5SDavid du Colombier 		return Error;
717*59cc4ca5SDavid du Colombier 	if(net == nil){
718*59cc4ca5SDavid du Colombier 		p = strrchr(conndir, '/');
719*59cc4ca5SDavid du Colombier 		*p = 0;
720*59cc4ca5SDavid du Colombier 		snprint(tcpdir, sizeof(tcpdir), conndir);
721*59cc4ca5SDavid du Colombier 	}
722*59cc4ca5SDavid du Colombier 
723*59cc4ca5SDavid du Colombier 	initibuf();
724*59cc4ca5SDavid du Colombier 
725*59cc4ca5SDavid du Colombier 	rv = hello(ctl);
726*59cc4ca5SDavid du Colombier 	if(rv < 0)
727*59cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
728*59cc4ca5SDavid du Colombier 
729*59cc4ca5SDavid du Colombier 	rv = logon(ctl);
730*59cc4ca5SDavid du Colombier 	if(rv < 0)
731*59cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
732*59cc4ca5SDavid du Colombier 
733*59cc4ca5SDavid du Colombier 	rv = xfertype(ctl, "I");
734*59cc4ca5SDavid du Colombier 	if(rv < 0)
735*59cc4ca5SDavid du Colombier 		return terminateftp(ctl, rv);
736*59cc4ca5SDavid du Colombier 
737*59cc4ca5SDavid du Colombier 	/* if file is up to date and the right size, stop */
738*59cc4ca5SDavid du Colombier 	if(ftprestart(ctl, out, u, r, mtime) > 0){
739*59cc4ca5SDavid du Colombier 		close(ctl);
740*59cc4ca5SDavid du Colombier 		return Eof;
741*59cc4ca5SDavid du Colombier 	}
742*59cc4ca5SDavid du Colombier 
743*59cc4ca5SDavid du Colombier 	/* first try passive mode, then active */
744*59cc4ca5SDavid du Colombier 	data = passive(ctl, u);
745*59cc4ca5SDavid du Colombier 	if(data < 0){
746*59cc4ca5SDavid du Colombier 		data = active(ctl, u);
747*59cc4ca5SDavid du Colombier 		if(data < 0)
748*59cc4ca5SDavid du Colombier 			return Error;
749*59cc4ca5SDavid du Colombier 	}
750*59cc4ca5SDavid du Colombier 
751*59cc4ca5SDavid du Colombier 	/* fork */
752*59cc4ca5SDavid du Colombier 	switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
753*59cc4ca5SDavid du Colombier 	case -1:
754*59cc4ca5SDavid du Colombier 		close(data);
755*59cc4ca5SDavid du Colombier 		return terminateftp(ctl, Error);
756*59cc4ca5SDavid du Colombier 	case 0:
757*59cc4ca5SDavid du Colombier 		ftpxfer(data, out, r);
758*59cc4ca5SDavid du Colombier 		close(data);
759*59cc4ca5SDavid du Colombier 		_exits(0);
760*59cc4ca5SDavid du Colombier 	default:
761*59cc4ca5SDavid du Colombier 		close(data);
762*59cc4ca5SDavid du Colombier 		break;
763*59cc4ca5SDavid du Colombier 	}
764*59cc4ca5SDavid du Colombier 
765*59cc4ca5SDavid du Colombier 	/* wait for reply message */
766*59cc4ca5SDavid du Colombier 	rv = ftprcode(ctl, msg, sizeof(msg));
767*59cc4ca5SDavid du Colombier 	close(ctl);
768*59cc4ca5SDavid du Colombier 
769*59cc4ca5SDavid du Colombier 	/* wait for process to terminate */
770*59cc4ca5SDavid du Colombier 	for(;;){
771*59cc4ca5SDavid du Colombier 		wrv = wait(&w);
772*59cc4ca5SDavid du Colombier 		if(wrv < 0)
773*59cc4ca5SDavid du Colombier 			return Error;
774*59cc4ca5SDavid du Colombier 		if(wrv == pid){
775*59cc4ca5SDavid du Colombier 			if(w.msg[0] == 0)
776*59cc4ca5SDavid du Colombier 				break;
777*59cc4ca5SDavid du Colombier 			werrstr("xfer: %s", w.msg);
778*59cc4ca5SDavid du Colombier 			return Error;
779*59cc4ca5SDavid du Colombier 		}
780*59cc4ca5SDavid du Colombier 	}
781*59cc4ca5SDavid du Colombier 
782*59cc4ca5SDavid du Colombier 	switch(rv){
783*59cc4ca5SDavid du Colombier 	case Success:
784*59cc4ca5SDavid du Colombier 		return Eof;
785*59cc4ca5SDavid du Colombier 	case TempFail:
786*59cc4ca5SDavid du Colombier 		return Server;
787*59cc4ca5SDavid du Colombier 	default:
788*59cc4ca5SDavid du Colombier 		return Error;
789*59cc4ca5SDavid du Colombier 	}
790*59cc4ca5SDavid du Colombier }
791*59cc4ca5SDavid du Colombier 
792*59cc4ca5SDavid du Colombier int
793*59cc4ca5SDavid du Colombier ftpcmd(int ctl, char *fmt, ...)
794*59cc4ca5SDavid du Colombier {
795*59cc4ca5SDavid du Colombier 	va_list arg;
796*59cc4ca5SDavid du Colombier 	char buf[2*1024], *s;
797*59cc4ca5SDavid du Colombier 
798*59cc4ca5SDavid du Colombier 	va_start(arg, fmt);
799*59cc4ca5SDavid du Colombier 	s = doprint(buf, buf + (sizeof(buf)-4) / sizeof(*buf), fmt, arg);
800*59cc4ca5SDavid du Colombier 	va_end(arg);
801*59cc4ca5SDavid du Colombier 	if(debug)
802*59cc4ca5SDavid du Colombier 		fprint(2, "%d -> %s\n", ctl, buf);
803*59cc4ca5SDavid du Colombier 	*s++ = '\r';
804*59cc4ca5SDavid du Colombier 	*s++ = '\n';
805*59cc4ca5SDavid du Colombier 	if(write(ctl, buf, s - buf) != s - buf)
806*59cc4ca5SDavid du Colombier 		return -1;
807*59cc4ca5SDavid du Colombier 	return 0;
808*59cc4ca5SDavid du Colombier }
809*59cc4ca5SDavid du Colombier 
810*59cc4ca5SDavid du Colombier int
811*59cc4ca5SDavid du Colombier ftprcode(int ctl, char *msg, int len)
812*59cc4ca5SDavid du Colombier {
813*59cc4ca5SDavid du Colombier 	int rv;
814*59cc4ca5SDavid du Colombier 	int i;
815*59cc4ca5SDavid du Colombier 
816*59cc4ca5SDavid du Colombier 	len--;	/* room for terminating null */
817*59cc4ca5SDavid du Colombier 	for(;;){
818*59cc4ca5SDavid du Colombier 		*msg = 0;
819*59cc4ca5SDavid du Colombier 		i = readline(ctl, msg, len);
820*59cc4ca5SDavid du Colombier 		if(i < 0)
821*59cc4ca5SDavid du Colombier 			break;
822*59cc4ca5SDavid du Colombier 		if(debug)
823*59cc4ca5SDavid du Colombier 			fprint(2, "%d <- %s\n", ctl, msg);
824*59cc4ca5SDavid du Colombier 
825*59cc4ca5SDavid du Colombier 		/* stop if not a continuation */
826*59cc4ca5SDavid du Colombier 		rv = atoi(msg);
827*59cc4ca5SDavid du Colombier 		if(rv >= 100 && rv < 600 && (i > 3 && msg[3] == ' '))
828*59cc4ca5SDavid du Colombier 			return rv/100;
829*59cc4ca5SDavid du Colombier 	}
830*59cc4ca5SDavid du Colombier 	*msg = 0;
831*59cc4ca5SDavid du Colombier 
832*59cc4ca5SDavid du Colombier 	return -1;
833*59cc4ca5SDavid du Colombier }
834*59cc4ca5SDavid du Colombier 
835*59cc4ca5SDavid du Colombier int
836*59cc4ca5SDavid du Colombier hello(int ctl)
837*59cc4ca5SDavid du Colombier {
838*59cc4ca5SDavid du Colombier 	char msg[1024];
839*59cc4ca5SDavid du Colombier 
840*59cc4ca5SDavid du Colombier 	/* wait for hello from other side */
841*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
842*59cc4ca5SDavid du Colombier 		werrstr("HELLO: %s", msg);
843*59cc4ca5SDavid du Colombier 		return Server;
844*59cc4ca5SDavid du Colombier 	}
845*59cc4ca5SDavid du Colombier 	return 0;
846*59cc4ca5SDavid du Colombier }
847*59cc4ca5SDavid du Colombier 
848*59cc4ca5SDavid du Colombier int
849*59cc4ca5SDavid du Colombier getdec(char *p, int n)
850*59cc4ca5SDavid du Colombier {
851*59cc4ca5SDavid du Colombier 	int x = 0;
852*59cc4ca5SDavid du Colombier 	int i;
853*59cc4ca5SDavid du Colombier 
854*59cc4ca5SDavid du Colombier 	for(i = 0; i < n; i++)
855*59cc4ca5SDavid du Colombier 		x = x*10 + (*p++ - '0');
856*59cc4ca5SDavid du Colombier 	return x;
857*59cc4ca5SDavid du Colombier }
858*59cc4ca5SDavid du Colombier 
859*59cc4ca5SDavid du Colombier int
860*59cc4ca5SDavid du Colombier ftprestart(int ctl, int out, URL *u, Range *r, long mtime)
861*59cc4ca5SDavid du Colombier {
862*59cc4ca5SDavid du Colombier 	Tm tm;
863*59cc4ca5SDavid du Colombier 	char msg[1024];
864*59cc4ca5SDavid du Colombier 	long x, rmtime;
865*59cc4ca5SDavid du Colombier 
866*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "MDTM %s", u->page);
867*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
868*59cc4ca5SDavid du Colombier 		r->start = 0;
869*59cc4ca5SDavid du Colombier 		return 0;		/* need to do something */
870*59cc4ca5SDavid du Colombier 	}
871*59cc4ca5SDavid du Colombier 
872*59cc4ca5SDavid du Colombier 	/* decode modification time */
873*59cc4ca5SDavid du Colombier 	if(strlen(msg) < 4 + 4 + 2 + 2 + 2 + 2 + 2){
874*59cc4ca5SDavid du Colombier 		r->start = 0;
875*59cc4ca5SDavid du Colombier 		return 0;		/* need to do something */
876*59cc4ca5SDavid du Colombier 	}
877*59cc4ca5SDavid du Colombier 	memset(&tm, 0, sizeof(tm));
878*59cc4ca5SDavid du Colombier 	tm.year = getdec(msg+4, 4) - 1900;
879*59cc4ca5SDavid du Colombier 	tm.mon = getdec(msg+4+4, 2) - 1;
880*59cc4ca5SDavid du Colombier 	tm.mday = getdec(msg+4+4+2, 2);
881*59cc4ca5SDavid du Colombier 	tm.hour = getdec(msg+4+4+2+2, 2);
882*59cc4ca5SDavid du Colombier 	tm.min = getdec(msg+4+4+2+2+2, 2);
883*59cc4ca5SDavid du Colombier 	tm.sec = getdec(msg+4+4+2+2+2+2, 2);
884*59cc4ca5SDavid du Colombier 	strcpy(tm.zone, "GMT");
885*59cc4ca5SDavid du Colombier 	rmtime = tm2sec(&tm);
886*59cc4ca5SDavid du Colombier 	if(rmtime > mtime)
887*59cc4ca5SDavid du Colombier 		r->start = 0;
888*59cc4ca5SDavid du Colombier 
889*59cc4ca5SDavid du Colombier 	/* get size */
890*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "SIZE %s", u->page);
891*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) == Success){
892*59cc4ca5SDavid du Colombier 		x = atol(msg+4);
893*59cc4ca5SDavid du Colombier 		if(r->start == x)
894*59cc4ca5SDavid du Colombier 			return 1;	/* we're up to date */
895*59cc4ca5SDavid du Colombier 		r->end = x;
896*59cc4ca5SDavid du Colombier 	}
897*59cc4ca5SDavid du Colombier 
898*59cc4ca5SDavid du Colombier 	/* seek to restart point */
899*59cc4ca5SDavid du Colombier 	if(r->start > 0){
900*59cc4ca5SDavid du Colombier 		ftpcmd(ctl, "REST %lud", r->start);
901*59cc4ca5SDavid du Colombier 		if(ftprcode(ctl, msg, sizeof(msg)) == Incomplete)
902*59cc4ca5SDavid du Colombier 			seek(out, r->start, 0);
903*59cc4ca5SDavid du Colombier 		else
904*59cc4ca5SDavid du Colombier 			r->start = 0;
905*59cc4ca5SDavid du Colombier 	}
906*59cc4ca5SDavid du Colombier 
907*59cc4ca5SDavid du Colombier 	return 0;	/* need to do something */
908*59cc4ca5SDavid du Colombier }
909*59cc4ca5SDavid du Colombier 
910*59cc4ca5SDavid du Colombier int
911*59cc4ca5SDavid du Colombier logon(int ctl)
912*59cc4ca5SDavid du Colombier {
913*59cc4ca5SDavid du Colombier 	char msg[1024];
914*59cc4ca5SDavid du Colombier 
915*59cc4ca5SDavid du Colombier 	/* login anonymous */
916*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "USER anonymous");
917*59cc4ca5SDavid du Colombier 	switch(ftprcode(ctl, msg, sizeof(msg))){
918*59cc4ca5SDavid du Colombier 	case Success:
919*59cc4ca5SDavid du Colombier 		return 0;
920*59cc4ca5SDavid du Colombier 	case Incomplete:
921*59cc4ca5SDavid du Colombier 		break;	/* need password */
922*59cc4ca5SDavid du Colombier 	default:
923*59cc4ca5SDavid du Colombier 		werrstr("USER: %s", msg);
924*59cc4ca5SDavid du Colombier 		return Server;
925*59cc4ca5SDavid du Colombier 	}
926*59cc4ca5SDavid du Colombier 
927*59cc4ca5SDavid du Colombier 	/* send user id as password */
928*59cc4ca5SDavid du Colombier 	sprint(msg, "%s@closedmind.org", getuser());
929*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PASS %s", msg);
930*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
931*59cc4ca5SDavid du Colombier 		werrstr("PASS: %s", msg);
932*59cc4ca5SDavid du Colombier 		return Server;
933*59cc4ca5SDavid du Colombier 	}
934*59cc4ca5SDavid du Colombier 
935*59cc4ca5SDavid du Colombier 	return 0;
936*59cc4ca5SDavid du Colombier }
937*59cc4ca5SDavid du Colombier 
938*59cc4ca5SDavid du Colombier int
939*59cc4ca5SDavid du Colombier xfertype(int ctl, char *t)
940*59cc4ca5SDavid du Colombier {
941*59cc4ca5SDavid du Colombier 	char msg[1024];
942*59cc4ca5SDavid du Colombier 
943*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "TYPE %s", t);
944*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
945*59cc4ca5SDavid du Colombier 		werrstr("TYPE %s: %s", t, msg);
946*59cc4ca5SDavid du Colombier 		return Server;
947*59cc4ca5SDavid du Colombier 	}
948*59cc4ca5SDavid du Colombier 
949*59cc4ca5SDavid du Colombier 	return 0;
950*59cc4ca5SDavid du Colombier }
951*59cc4ca5SDavid du Colombier 
952*59cc4ca5SDavid du Colombier int
953*59cc4ca5SDavid du Colombier passive(int ctl, URL *u)
954*59cc4ca5SDavid du Colombier {
955*59cc4ca5SDavid du Colombier 	char msg[1024];
956*59cc4ca5SDavid du Colombier 	char ipaddr[32];
957*59cc4ca5SDavid du Colombier 	char *f[6];
958*59cc4ca5SDavid du Colombier 	char *p;
959*59cc4ca5SDavid du Colombier 	int fd;
960*59cc4ca5SDavid du Colombier 	int port;
961*59cc4ca5SDavid du Colombier 	char aport[12];
962*59cc4ca5SDavid du Colombier 
963*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PASV");
964*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success)
965*59cc4ca5SDavid du Colombier 		return Error;
966*59cc4ca5SDavid du Colombier 
967*59cc4ca5SDavid du Colombier 	/* get address and port number from reply, this is AI */
968*59cc4ca5SDavid du Colombier 	p = strchr(msg, '(');
969*59cc4ca5SDavid du Colombier 	if(p == nil){
970*59cc4ca5SDavid du Colombier 		for(p = msg+3; *p; p++)
971*59cc4ca5SDavid du Colombier 			if(isdigit(*p))
972*59cc4ca5SDavid du Colombier 				break;
973*59cc4ca5SDavid du Colombier 	} else
974*59cc4ca5SDavid du Colombier 		p++;
975*59cc4ca5SDavid du Colombier 	if(getfields(p, f, 6, 0, ",)") < 6){
976*59cc4ca5SDavid du Colombier 		werrstr("ftp protocol botch");
977*59cc4ca5SDavid du Colombier 		return Server;
978*59cc4ca5SDavid du Colombier 	}
979*59cc4ca5SDavid du Colombier 	snprint(ipaddr, sizeof(ipaddr), "%s.%s.%s.%s",
980*59cc4ca5SDavid du Colombier 		f[0], f[1], f[2], f[3]);
981*59cc4ca5SDavid du Colombier 	port = ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff);
982*59cc4ca5SDavid du Colombier 	sprint(aport, "%d", port);
983*59cc4ca5SDavid du Colombier 
984*59cc4ca5SDavid du Colombier 	/* open data connection */
985*59cc4ca5SDavid du Colombier 	fd = dial(netmkaddr(ipaddr, tcpdir, aport), 0, 0, 0);
986*59cc4ca5SDavid du Colombier 	if(fd < 0){
987*59cc4ca5SDavid du Colombier 		werrstr("passive mode failed: %r");
988*59cc4ca5SDavid du Colombier 		return Error;
989*59cc4ca5SDavid du Colombier 	}
990*59cc4ca5SDavid du Colombier 
991*59cc4ca5SDavid du Colombier 	/* tell remote to send a file */
992*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "RETR %s", u->page);
993*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
994*59cc4ca5SDavid du Colombier 		werrstr("RETR %s: %s", u->page, msg);
995*59cc4ca5SDavid du Colombier 		return Error;
996*59cc4ca5SDavid du Colombier 	}
997*59cc4ca5SDavid du Colombier 	return fd;
998*59cc4ca5SDavid du Colombier }
999*59cc4ca5SDavid du Colombier 
1000*59cc4ca5SDavid du Colombier int
1001*59cc4ca5SDavid du Colombier active(int ctl, URL *u)
1002*59cc4ca5SDavid du Colombier {
1003*59cc4ca5SDavid du Colombier 	char msg[1024];
1004*59cc4ca5SDavid du Colombier 	char dir[40], ldir[40];
1005*59cc4ca5SDavid du Colombier 	uchar ipaddr[4];
1006*59cc4ca5SDavid du Colombier 	uchar port[2];
1007*59cc4ca5SDavid du Colombier 	int lcfd, dfd, afd;
1008*59cc4ca5SDavid du Colombier 
1009*59cc4ca5SDavid du Colombier 	/* announce a port for the call back */
1010*59cc4ca5SDavid du Colombier 	snprint(msg, sizeof(msg), "%s!*!0", tcpdir);
1011*59cc4ca5SDavid du Colombier 	afd = announce(msg, dir);
1012*59cc4ca5SDavid du Colombier 	if(afd < 0)
1013*59cc4ca5SDavid du Colombier 		return Error;
1014*59cc4ca5SDavid du Colombier 
1015*59cc4ca5SDavid du Colombier 	/* get a local address/port of the annoucement */
1016*59cc4ca5SDavid du Colombier 	if(getaddrport(dir, ipaddr, port) < 0){
1017*59cc4ca5SDavid du Colombier 		close(afd);
1018*59cc4ca5SDavid du Colombier 		return Error;
1019*59cc4ca5SDavid du Colombier 	}
1020*59cc4ca5SDavid du Colombier 
1021*59cc4ca5SDavid du Colombier 	/* tell remote side address and port*/
1022*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "PORT %d,%d,%d,%d,%d,%d", ipaddr[0], ipaddr[1], ipaddr[2],
1023*59cc4ca5SDavid du Colombier 		ipaddr[3], port[0], port[1]);
1024*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Success){
1025*59cc4ca5SDavid du Colombier 		close(afd);
1026*59cc4ca5SDavid du Colombier 		werrstr("active: %s", msg);
1027*59cc4ca5SDavid du Colombier 		return Error;
1028*59cc4ca5SDavid du Colombier 	}
1029*59cc4ca5SDavid du Colombier 
1030*59cc4ca5SDavid du Colombier 	/* tell remote to send a file */
1031*59cc4ca5SDavid du Colombier 	ftpcmd(ctl, "RETR %s", u->page);
1032*59cc4ca5SDavid du Colombier 	if(ftprcode(ctl, msg, sizeof(msg)) != Extra){
1033*59cc4ca5SDavid du Colombier 		close(afd);
1034*59cc4ca5SDavid du Colombier 		werrstr("RETR: %s", msg);
1035*59cc4ca5SDavid du Colombier 		return Server;
1036*59cc4ca5SDavid du Colombier 	}
1037*59cc4ca5SDavid du Colombier 
1038*59cc4ca5SDavid du Colombier 	/* wait for a connection */
1039*59cc4ca5SDavid du Colombier 	lcfd = listen(dir, ldir);
1040*59cc4ca5SDavid du Colombier 	if(lcfd < 0){
1041*59cc4ca5SDavid du Colombier 		close(afd);
1042*59cc4ca5SDavid du Colombier 		return Error;
1043*59cc4ca5SDavid du Colombier 	}
1044*59cc4ca5SDavid du Colombier 	dfd = accept(lcfd, ldir);
1045*59cc4ca5SDavid du Colombier 	if(dfd < 0){
1046*59cc4ca5SDavid du Colombier 		close(afd);
1047*59cc4ca5SDavid du Colombier 		close(lcfd);
1048*59cc4ca5SDavid du Colombier 		return Error;
1049*59cc4ca5SDavid du Colombier 	}
1050*59cc4ca5SDavid du Colombier 	close(afd);
1051*59cc4ca5SDavid du Colombier 	close(lcfd);
1052*59cc4ca5SDavid du Colombier 
1053*59cc4ca5SDavid du Colombier 	return dfd;
1054*59cc4ca5SDavid du Colombier }
1055*59cc4ca5SDavid du Colombier 
1056*59cc4ca5SDavid du Colombier int
1057*59cc4ca5SDavid du Colombier ftpxfer(int in, int out, Range *r)
1058*59cc4ca5SDavid du Colombier {
1059*59cc4ca5SDavid du Colombier 	char buf[1024];
1060*59cc4ca5SDavid du Colombier 	long vtime;
1061*59cc4ca5SDavid du Colombier 	int i, n;
1062*59cc4ca5SDavid du Colombier 
1063*59cc4ca5SDavid du Colombier 	vtime = 0;
1064*59cc4ca5SDavid du Colombier 	for(n = 0;;n += i){
1065*59cc4ca5SDavid du Colombier 		i = read(in, buf, sizeof(buf));
1066*59cc4ca5SDavid du Colombier 		if(i == 0)
1067*59cc4ca5SDavid du Colombier 			break;
1068*59cc4ca5SDavid du Colombier 		if(i < 0)
1069*59cc4ca5SDavid du Colombier 			return Error;
1070*59cc4ca5SDavid du Colombier 		if(write(out, buf, i) != i)
1071*59cc4ca5SDavid du Colombier 			return Error;
1072*59cc4ca5SDavid du Colombier 		r->start += i;
1073*59cc4ca5SDavid du Colombier 		if(verbose && vtime != time(0)) {
1074*59cc4ca5SDavid du Colombier 			vtime = time(0);
1075*59cc4ca5SDavid du Colombier 			fprint(2, "%ld %ld\n", r->start, r->end);
1076*59cc4ca5SDavid du Colombier 		}
1077*59cc4ca5SDavid du Colombier 	}
1078*59cc4ca5SDavid du Colombier 	return n;
1079*59cc4ca5SDavid du Colombier }
1080*59cc4ca5SDavid du Colombier 
1081*59cc4ca5SDavid du Colombier int
1082*59cc4ca5SDavid du Colombier terminateftp(int ctl, int rv)
1083*59cc4ca5SDavid du Colombier {
1084*59cc4ca5SDavid du Colombier 	close(ctl);
1085*59cc4ca5SDavid du Colombier 	return rv;
1086*59cc4ca5SDavid du Colombier }
1087*59cc4ca5SDavid du Colombier 
1088*59cc4ca5SDavid du Colombier /*
1089*59cc4ca5SDavid du Colombier  * case insensitive strcmp (why aren't these in libc?)
1090*59cc4ca5SDavid du Colombier  */
10917dd7cddfSDavid du Colombier int
10927dd7cddfSDavid du Colombier cistrncmp(char *a, char *b, int n)
10937dd7cddfSDavid du Colombier {
10947dd7cddfSDavid du Colombier 	while(n-- > 0){
10957dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
10967dd7cddfSDavid du Colombier 			return -1;
10977dd7cddfSDavid du Colombier 	}
10987dd7cddfSDavid du Colombier 	return 0;
10997dd7cddfSDavid du Colombier }
11007dd7cddfSDavid du Colombier 
11017dd7cddfSDavid du Colombier int
11027dd7cddfSDavid du Colombier cistrcmp(char *a, char *b)
11037dd7cddfSDavid du Colombier {
11047dd7cddfSDavid du Colombier 	while(*a || *b)
11057dd7cddfSDavid du Colombier 		if(tolower(*a++) != tolower(*b++))
11067dd7cddfSDavid du Colombier 			return -1;
11077dd7cddfSDavid du Colombier 
11087dd7cddfSDavid du Colombier 	return 0;
11097dd7cddfSDavid du Colombier }
11107dd7cddfSDavid du Colombier 
11117dd7cddfSDavid du Colombier /*
11127dd7cddfSDavid du Colombier  *  buffered io
11137dd7cddfSDavid du Colombier  */
11147dd7cddfSDavid du Colombier struct
11157dd7cddfSDavid du Colombier {
11167dd7cddfSDavid du Colombier 	char *rp;
11177dd7cddfSDavid du Colombier 	char *wp;
11187dd7cddfSDavid du Colombier 	char buf[4*1024];
11197dd7cddfSDavid du Colombier } b;
11207dd7cddfSDavid du Colombier 
11217dd7cddfSDavid du Colombier void
11227dd7cddfSDavid du Colombier initibuf(void)
11237dd7cddfSDavid du Colombier {
11247dd7cddfSDavid du Colombier 	b.rp = b.wp = b.buf;
11257dd7cddfSDavid du Colombier }
11267dd7cddfSDavid du Colombier 
1127*59cc4ca5SDavid du Colombier /*
1128*59cc4ca5SDavid du Colombier  *  read a possibly buffered line, strip off trailing while
1129*59cc4ca5SDavid du Colombier  */
11307dd7cddfSDavid du Colombier int
11317dd7cddfSDavid du Colombier readline(int fd, char *buf, int len)
11327dd7cddfSDavid du Colombier {
11337dd7cddfSDavid du Colombier 	int n;
11347dd7cddfSDavid du Colombier 	char *p;
1135*59cc4ca5SDavid du Colombier 	int eof = 0;
11367dd7cddfSDavid du Colombier 
11377dd7cddfSDavid du Colombier 	len--;
11387dd7cddfSDavid du Colombier 
11397dd7cddfSDavid du Colombier 	for(p = buf;;){
11407dd7cddfSDavid du Colombier 		if(b.rp >= b.wp){
11417dd7cddfSDavid du Colombier 			n = read(fd, b.wp, sizeof(b.buf)/2);
11427dd7cddfSDavid du Colombier 			if(n < 0)
11437dd7cddfSDavid du Colombier 				return -1;
1144*59cc4ca5SDavid du Colombier 			if(n == 0){
1145*59cc4ca5SDavid du Colombier 				eof = 1;
11467dd7cddfSDavid du Colombier 				break;
1147*59cc4ca5SDavid du Colombier 			}
11487dd7cddfSDavid du Colombier 			b.wp += n;
11497dd7cddfSDavid du Colombier 		}
11507dd7cddfSDavid du Colombier 		n = *b.rp++;
11517dd7cddfSDavid du Colombier 		if(len > 0){
11527dd7cddfSDavid du Colombier 			*p++ = n;
11537dd7cddfSDavid du Colombier 			len--;
11547dd7cddfSDavid du Colombier 		}
11557dd7cddfSDavid du Colombier 		if(n == '\n')
11567dd7cddfSDavid du Colombier 			break;
11577dd7cddfSDavid du Colombier 	}
11587dd7cddfSDavid du Colombier 
11597dd7cddfSDavid du Colombier 	/* drop trailing white */
11607dd7cddfSDavid du Colombier 	for(;;){
11617dd7cddfSDavid du Colombier 		if(p <= buf)
11627dd7cddfSDavid du Colombier 			break;
11637dd7cddfSDavid du Colombier 		n = *(p-1);
11647dd7cddfSDavid du Colombier 		if(n != ' ' && n != '\t' && n != '\r' && n != '\n')
11657dd7cddfSDavid du Colombier 			break;
11667dd7cddfSDavid du Colombier 		p--;
11677dd7cddfSDavid du Colombier 	}
11687dd7cddfSDavid du Colombier 	*p = 0;
1169*59cc4ca5SDavid du Colombier 
1170*59cc4ca5SDavid du Colombier 	if(eof && p == buf)
1171*59cc4ca5SDavid du Colombier 		return -1;
1172*59cc4ca5SDavid du Colombier 
11737dd7cddfSDavid du Colombier 	return p-buf;
11747dd7cddfSDavid du Colombier }
11757dd7cddfSDavid du Colombier 
11767dd7cddfSDavid du Colombier void
11777dd7cddfSDavid du Colombier unreadline(char *line)
11787dd7cddfSDavid du Colombier {
11797dd7cddfSDavid du Colombier 	int i, n;
11807dd7cddfSDavid du Colombier 
11817dd7cddfSDavid du Colombier 	i = strlen(line);
11827dd7cddfSDavid du Colombier 	n = b.wp-b.rp;
11837dd7cddfSDavid du Colombier 	memmove(&b.buf[i+1], b.rp, n);
11847dd7cddfSDavid du Colombier 	memmove(b.buf, line, i);
11857dd7cddfSDavid du Colombier 	b.buf[i] = '\n';
11867dd7cddfSDavid du Colombier 	b.rp = b.buf;
11877dd7cddfSDavid du Colombier 	b.wp = b.rp + i + 1 + n;
11887dd7cddfSDavid du Colombier }
11897dd7cddfSDavid du Colombier 
11907dd7cddfSDavid du Colombier int
11917dd7cddfSDavid du Colombier readibuf(int fd, char *buf, int len)
11927dd7cddfSDavid du Colombier {
11937dd7cddfSDavid du Colombier 	int n;
11947dd7cddfSDavid du Colombier 
11957dd7cddfSDavid du Colombier 	n = b.wp-b.rp;
11967dd7cddfSDavid du Colombier 	if(n > 0){
11977dd7cddfSDavid du Colombier 		if(n > len)
11987dd7cddfSDavid du Colombier 			n = len;
11997dd7cddfSDavid du Colombier 		memmove(buf, b.rp, n);
12007dd7cddfSDavid du Colombier 		b.rp += n;
12017dd7cddfSDavid du Colombier 		return n;
12027dd7cddfSDavid du Colombier 	}
12037dd7cddfSDavid du Colombier 	return read(fd, buf, len);
12047dd7cddfSDavid du Colombier }
12057dd7cddfSDavid du Colombier 
12067dd7cddfSDavid du Colombier int
12077dd7cddfSDavid du Colombier dfprint(int fd, char *fmt, ...)
12087dd7cddfSDavid du Colombier {
12097dd7cddfSDavid du Colombier 	char buf[4*1024];
12107dd7cddfSDavid du Colombier 	va_list arg;
12117dd7cddfSDavid du Colombier 
12127dd7cddfSDavid du Colombier 	va_start(arg, fmt);
12137dd7cddfSDavid du Colombier 	doprint(buf, buf+sizeof(buf), fmt, arg);
12147dd7cddfSDavid du Colombier 	va_end(arg);
12157dd7cddfSDavid du Colombier 	if(debug)
12167dd7cddfSDavid du Colombier 		fprint(2, "%d -> %s", fd, buf);
12177dd7cddfSDavid du Colombier 	return fprint(fd, "%s", buf);
12187dd7cddfSDavid du Colombier }
1219*59cc4ca5SDavid du Colombier 
1220*59cc4ca5SDavid du Colombier int
1221*59cc4ca5SDavid du Colombier getaddrport(char *dir, uchar *ipaddr, uchar *port)
1222*59cc4ca5SDavid du Colombier {
1223*59cc4ca5SDavid du Colombier 	char buf[256];
1224*59cc4ca5SDavid du Colombier 	int fd, i;
1225*59cc4ca5SDavid du Colombier 	char *p;
1226*59cc4ca5SDavid du Colombier 
1227*59cc4ca5SDavid du Colombier 	snprint(buf, sizeof(buf), "%s/local", dir);
1228*59cc4ca5SDavid du Colombier 	fd = open(buf, OREAD);
1229*59cc4ca5SDavid du Colombier 	if(fd < 0)
1230*59cc4ca5SDavid du Colombier 		return -1;
1231*59cc4ca5SDavid du Colombier 	i = read(fd, buf, sizeof(buf)-1);
1232*59cc4ca5SDavid du Colombier 	close(fd);
1233*59cc4ca5SDavid du Colombier 	if(i <= 0)
1234*59cc4ca5SDavid du Colombier 		return -1;
1235*59cc4ca5SDavid du Colombier 	buf[i] = 0;
1236*59cc4ca5SDavid du Colombier 	p = strchr(buf, '!');
1237*59cc4ca5SDavid du Colombier 	if(p != nil)
1238*59cc4ca5SDavid du Colombier 		*p++ = 0;
1239*59cc4ca5SDavid du Colombier 	v4parseip(ipaddr, buf);
1240*59cc4ca5SDavid du Colombier 	i = atoi(p);
1241*59cc4ca5SDavid du Colombier 	port[0] = i>>8;
1242*59cc4ca5SDavid du Colombier 	port[1] = i;
1243*59cc4ca5SDavid du Colombier 	return 0;
1244*59cc4ca5SDavid du Colombier }
1245