henesyinit from p9fd787fda5 years agoHistory
1269 lines · 21.0 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ctype.h>#include <bio.h>#include <ip.h> typedef struct URL URL;struct URL{	int	method;	char	*host;	char	*port;	char	*page;	char	*etag;	char	*redirect;	char	*postbody;	long	mtime;}; typedef struct Range Range;struct Range{	long	start;	/* only 2 gig supported, tdb */	long	end;}; enum{	Http,	Ftp,	Other}; enum{	Eof = 0,	Error = -1,	Server = -2,	Changed = -3,}; int debug;char *ofile; int	doftp(URL*, Range*, int, long);int	dohttp(URL*, Range*, int, long);int	crackurl(URL*, char*);Range*	crackrange(char*);int	getheader(int, char*, int);int	httpheaders(int, int, URL*, Range*);int	httprcode(int);int	cistrncmp(char*, char*, int);int	cistrcmp(char*, char*);void	initibuf(void);int	readline(int, char*, int);int	readibuf(int, char*, int);int	dfprint(int, char*, ...);void	unreadline(char*); int	verbose;char	*net;char	tcpdir[64]; struct {	char	*name;	int	(*f)(URL*, Range*, int, long);} method[] = {	[Http]	{ "http",	dohttp },	[Ftp]	{ "ftp",	doftp },	[Other]	{ "_______",	nil },}; voidusage(void){	fprint(2, "usage: %s [-v] [-o outfile] [-p body] [-x netmtpt] url\n", argv0);	exits("usage");} voidmain(int argc, char **argv){	URL u;	Range r;	int fd, errs, n;	ulong mtime;	Dir *d;	char postbody[4096], *p, *e, *t; 	ofile = nil;	p = postbody;	e = p + sizeof(postbody);	r.start = 0;	r.end = -1;	mtime = 0;	memset(&u, 0, sizeof(u)); 	ARGBEGIN {	case 'o':		ofile = ARGF();		break;	case 'd':		debug = 1;		break;	case 'v':		verbose = 1;		break;	case 'x':		net = ARGF();		if(net == nil)			usage();		break;	case 'p':		t = ARGF();		if(t == nil)			usage();		if(p != postbody)			p = seprint(p, e, "&%s", t);		else			p = seprint(p, e, "%s", t);		u.postbody = postbody;				break;	default:		usage();	} ARGEND; 	if(net != nil){		if(strlen(net) > sizeof(tcpdir)-5)			sysfatal("network mount point too long");		snprint(tcpdir, sizeof(tcpdir), "%s/tcp", net);	} else		snprint(tcpdir, sizeof(tcpdir), "tcp"); 	if(argc != 1)		usage(); 	fd = 1;	if(ofile != nil){		d = dirstat(ofile);		if(d == nil){			fd = create(ofile, OWRITE, 0664);			if(fd < 0)				sysfatal("creating %s: %r", ofile);		} else {			fd = open(ofile, OWRITE);			if(fd < 0)				sysfatal("can't open %s: %r", ofile);			r.start = d->length;			mtime = d->mtime;			free(d);		}	} 	errs = 0; 	if(crackurl(&u, argv[0]) < 0)		sysfatal("%r"); 	for(;;){		/* transfer data */		werrstr("");		seek(fd, 0, 0);		n = (*method[u.method].f)(&u, &r, fd, mtime); 		switch(n){		case Eof:			exits(0);			break;		case Error:			if(errs++ < 10)				continue;			sysfatal("too many errors with no progress %r");			break;		case Server:			sysfatal("server returned: %r");			break;		} 		/* forward progress */		errs = 0;		r.start += n;		if(r.start >= r.end)			break;	} 	exits(0);} intcrackurl(URL *u, char *s){	char *p;	int i; 	if(u->host != nil){		free(u->host);		u->host = nil;	}	if(u->page != nil){		free(u->page);		u->page = nil;	} 	/* get type */	u->method = Other;	for(p = s; *p; p++){		if(*p == '/'){			u->method = Http;			p = s;			break;		}		if(*p == ':' && *(p+1)=='/' && *(p+2)=='/'){			*p = 0;			p += 3;			for(i = 0; i < nelem(method); i++){				if(cistrcmp(s, method[i].name) == 0){					u->method = i;					break;				}			}			break;		}	} 	if(u->method == Other){		werrstr("unsupported URL type %s", s);		return -1;	} 	/* get system */	s = p;	p = strchr(s, '/');	if(p == nil){		u->host = strdup(s);		u->page = strdup("/");	} else {		u->page = strdup(p);		*p = 0;		u->host = strdup(s);		*p = '/';	} 	if(p = strchr(u->host, ':')) {		*p++ = 0;		u->port = p;	} else 		u->port = method[u->method].name; 	if(*(u->host) == 0){		werrstr("bad url, null host");		return -1;	} 	return 0;} char *day[] = {	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *month[] = {	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; struct{	int	fd;	long	mtime;} note; voidcatch(void*, char*){	Dir d; 	nulldir(&d);	d.mtime = note.mtime;	if(dirfwstat(note.fd, &d) < 0)		sysfatal("catch: can't dirfwstat: %r");	noted(NDFLT);} intdohttp(URL *u, Range *r, int out, long mtime){	int fd, cfd;	int redirect, loop;	int n, rv, code;	long tot, vtime;	Tm *tm;	char buf[1024];	char err[ERRMAX];  	/*  always move back to a previous 512 byte bound because some	 *  servers can't seem to deal with requests that start at the	 *  end of the file	 */	if(r->start)		r->start = ((r->start-1)/512)*512; 	/* loop for redirects, requires reading both response code and headers */	fd = -1;	for(loop = 0; loop < 32; loop++){		fd = dial(netmkaddr(u->host, tcpdir, u->port), 0, 0, 0);		if(fd < 0)			return Error; 		/* write request, use range if not start of file */		if(u->postbody == nil){			dfprint(fd,	"GET %s HTTP/1.0\r\n"					"Host: %s\r\n"					"User-agent: Plan9/hget\r\n",					u->page, u->host);		} else {			dfprint(fd,	"POST %s HTTP/1.0\r\n"					"Host: %s\r\n"					"Content-type: application/x-www-form-urlencoded\r\n"					"Content-length: %d\r\n"					"User-agent: Plan9/hget\r\n"					"\r\n",					u->page, u->host, strlen(u->postbody));			dfprint(fd,	"%s", u->postbody);		}		if(r->start != 0){			dfprint(fd, "Range: bytes=%d-\n", r->start);			if(u->etag != nil){				dfprint(fd, "If-range: %s\n", u->etag);			} else {				tm = gmtime(mtime);				dfprint(fd, "If-range: %s, %d %s %d %2d:%2.2d:%2.2d GMT\n",					day[tm->wday], tm->mday, month[tm->mon],					tm->year+1900, tm->hour, tm->min, tm->sec);			}		}		if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){			if(fprint(cfd, "http://%s%s", u->host, u->page) > 0){				while((n = read(cfd, buf, sizeof buf)) > 0){					if(debug)						write(2, buf, n);					write(fd, buf, n);				}			}else{				close(cfd);				cfd = -1;			}		}		dfprint(fd, "\r\n", u->host); 		redirect = 0;		initibuf();		code = httprcode(fd);		switch(code){		case Error:	/* connection timed out */		case Eof:			close(fd);			close(cfd);			return code; 		case 200:	/* OK */		case 201:	/* Created */		case 202:	/* Accepted */			if(ofile == nil && r->start != 0)				sysfatal("page changed underfoot");			break; 		case 204:	/* No Content */			sysfatal("No Content"); 		case 206:	/* Partial Content */			seek(out, r->start, 0);			break; 		case 301:	/* Moved Permanently */		case 302:	/* Moved Temporarily */			redirect = 1;			u->postbody = nil;			break; 		case 304:	/* Not Modified */			break; 		case 400:	/* Bad Request */			sysfatal("Bad Request"); 		case 401:	/* Unauthorized */		case 402:	/* ??? */			sysfatal("Unauthorized"); 		case 403:	/* Forbidden */			sysfatal("Forbidden by server"); 		case 404:	/* Not Found */			sysfatal("Not found on server"); 		case 500:	/* Internal server error */			sysfatal("Server choked"); 		case 501:	/* Not implemented */			sysfatal("Server can't do it!"); 		case 502:	/* Bad gateway */			sysfatal("Bad gateway"); 		case 503:	/* Service unavailable */			sysfatal("Service unavailable");				default:			sysfatal("Unknown response code");		} 		if(u->redirect != nil){			free(u->redirect);			u->redirect = nil;		} 		rv = httpheaders(fd, cfd, u, r);		close(cfd);		if(rv != 0){			close(fd);			return rv;		} 		if(!redirect)			break; 		if(u->redirect == nil)			sysfatal("redirect: no URL");		if(crackurl(u, u->redirect) < 0)			sysfatal("redirect: %r");	} 	/* transfer whatever you get */	if(ofile != nil && u->mtime != 0){		note.fd = out;		note.mtime = u->mtime;		notify(catch);	} 	tot = 0;	vtime = 0;	for(;;){		n = readibuf(fd, buf, sizeof(buf));		if(n <= 0)			break;		if(write(out, buf, n) != n)			break;		tot += n;		if(verbose && vtime != time(0)) {			vtime = time(0);			fprint(2, "%ld %ld\n", r->start+tot, r->end);		}	}	notify(nil);	close(fd); 	if(ofile != nil && u->mtime != 0){		Dir d; 		rerrstr(err, sizeof err);		nulldir(&d);		d.mtime = u->mtime;		if(dirfwstat(out, &d) < 0)			fprint(2, "couldn't set mtime: %r\n");		errstr(err, sizeof err);	} 	return tot;} /* get the http response code */inthttprcode(int fd){	int n;	char *p;	char buf[256]; 	n = readline(fd, buf, sizeof(buf)-1);	if(n <= 0)		return n;	if(debug)		fprint(2, "%d <- %s\n", fd, buf);	p = strchr(buf, ' ');	if(strncmp(buf, "HTTP/", 5) != 0 || p == nil){		werrstr("bad response from server");		return -1;	}	buf[n] = 0;	return atoi(p+1);} /* read in and crack the http headers, update u and r */void	hhetag(char*, URL*, Range*);void	hhmtime(char*, URL*, Range*);void	hhclen(char*, URL*, Range*);void	hhcrange(char*, URL*, Range*);void	hhuri(char*, URL*, Range*);void	hhlocation(char*, URL*, Range*);

Lines 1–500 of 1269.