xref: /plan9/sys/src/cmd/webfs/http.c (revision 1066d6debf4f3ce80fbab98c906650d920c13a7a)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ip.h>
59a747e4fSDavid du Colombier #include <plumb.h>
69a747e4fSDavid du Colombier #include <thread.h>
79a747e4fSDavid du Colombier #include <fcall.h>
89a747e4fSDavid du Colombier #include <9p.h>
99dfc0cb2SDavid du Colombier #include <libsec.h>
109dfc0cb2SDavid du Colombier #include <auth.h>
119a747e4fSDavid du Colombier #include "dat.h"
129a747e4fSDavid du Colombier #include "fns.h"
139a747e4fSDavid du Colombier 
14*1066d6deSDavid du Colombier char PostContentType[] = "application/x-www-form-urlencoded";
159a747e4fSDavid du Colombier int httpdebug;
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier typedef struct HttpState HttpState;
189a747e4fSDavid du Colombier struct HttpState
199a747e4fSDavid du Colombier {
209a747e4fSDavid du Colombier 	int fd;
219a747e4fSDavid du Colombier 	Client *c;
229a747e4fSDavid du Colombier 	char *location;
239a747e4fSDavid du Colombier 	char *setcookie;
249a747e4fSDavid du Colombier 	char *netaddr;
259dfc0cb2SDavid du Colombier 	char *credentials;
269dfc0cb2SDavid du Colombier 	char autherror[ERRMAX];
279a747e4fSDavid du Colombier 	Ibuf	b;
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier 
309a747e4fSDavid du Colombier static void
319a747e4fSDavid du Colombier location(HttpState *hs, char *value)
329a747e4fSDavid du Colombier {
339a747e4fSDavid du Colombier 	if(hs->location == nil)
349a747e4fSDavid du Colombier 		hs->location = estrdup(value);
359a747e4fSDavid du Colombier }
369a747e4fSDavid du Colombier 
379a747e4fSDavid du Colombier static void
389a747e4fSDavid du Colombier contenttype(HttpState *hs, char *value)
399a747e4fSDavid du Colombier {
403b56890dSDavid du Colombier 	if(hs->c->contenttype != nil)
413b56890dSDavid du Colombier 		free(hs->c->contenttype);
429a747e4fSDavid du Colombier 	hs->c->contenttype = estrdup(value);
439a747e4fSDavid du Colombier }
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier static void
469a747e4fSDavid du Colombier setcookie(HttpState *hs, char *value)
479a747e4fSDavid du Colombier {
489a747e4fSDavid du Colombier 	char *s, *t;
499a747e4fSDavid du Colombier 	Fmt f;
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier 	s = hs->setcookie;
529a747e4fSDavid du Colombier 	fmtstrinit(&f);
539a747e4fSDavid du Colombier 	if(s)
549a747e4fSDavid du Colombier 		fmtprint(&f, "%s", s);
559a747e4fSDavid du Colombier 	fmtprint(&f, "set-cookie: ");
569a747e4fSDavid du Colombier 	fmtprint(&f, "%s", value);
579a747e4fSDavid du Colombier 	fmtprint(&f, "\n");
589a747e4fSDavid du Colombier 	t = fmtstrflush(&f);
599a747e4fSDavid du Colombier 	if(t){
609a747e4fSDavid du Colombier 		free(s);
619a747e4fSDavid du Colombier 		hs->setcookie = t;
629a747e4fSDavid du Colombier 	}
639a747e4fSDavid du Colombier }
649a747e4fSDavid du Colombier 
659dfc0cb2SDavid du Colombier static char*
669dfc0cb2SDavid du Colombier unquote(char *s, char **ps)
679dfc0cb2SDavid du Colombier {
689dfc0cb2SDavid du Colombier 	char *p;
699dfc0cb2SDavid du Colombier 
709dfc0cb2SDavid du Colombier 	if(*s != '"'){
719dfc0cb2SDavid du Colombier 		p = strpbrk(s, " \t\r\n");
729dfc0cb2SDavid du Colombier 		*p++ = 0;
739dfc0cb2SDavid du Colombier 		*ps = p;
749dfc0cb2SDavid du Colombier 		return s;
759dfc0cb2SDavid du Colombier 	}
769dfc0cb2SDavid du Colombier 	for(p=s+1; *p; p++){
779dfc0cb2SDavid du Colombier 		if(*p == '\"'){
789dfc0cb2SDavid du Colombier 			*p++ = 0;
799dfc0cb2SDavid du Colombier 			break;
809dfc0cb2SDavid du Colombier 		}
819dfc0cb2SDavid du Colombier 		if(*p == '\\' && *(p+1)){
829dfc0cb2SDavid du Colombier 			p++;
839dfc0cb2SDavid du Colombier 			continue;
849dfc0cb2SDavid du Colombier 		}
859dfc0cb2SDavid du Colombier 	}
869dfc0cb2SDavid du Colombier 	memmove(s, s+1, p-(s+1));
879dfc0cb2SDavid du Colombier 	s[p-(s+1)] = 0;
889dfc0cb2SDavid du Colombier 	*ps = p;
899dfc0cb2SDavid du Colombier 	return s;
909dfc0cb2SDavid du Colombier }
919dfc0cb2SDavid du Colombier 
929dfc0cb2SDavid du Colombier static char*
939dfc0cb2SDavid du Colombier servername(char *addr)
949dfc0cb2SDavid du Colombier {
959dfc0cb2SDavid du Colombier 	char *p;
969dfc0cb2SDavid du Colombier 
979dfc0cb2SDavid du Colombier 	if(strncmp(addr, "tcp!", 4) == 0
989dfc0cb2SDavid du Colombier 	|| strncmp(addr, "net!", 4) == 0)
999dfc0cb2SDavid du Colombier 		addr += 4;
1009dfc0cb2SDavid du Colombier 	addr = estrdup(addr);
1019dfc0cb2SDavid du Colombier 	p = addr+strlen(addr);
1029dfc0cb2SDavid du Colombier 	if(p>addr && *(p-1) == 's')
1039dfc0cb2SDavid du Colombier 		p--;
1049dfc0cb2SDavid du Colombier 	if(p>addr+5 && strcmp(p-5, "!http") == 0)
1059dfc0cb2SDavid du Colombier 		p[-5] = 0;
1069dfc0cb2SDavid du Colombier 	return addr;
1079dfc0cb2SDavid du Colombier }
1089dfc0cb2SDavid du Colombier 
1099dfc0cb2SDavid du Colombier void
1109dfc0cb2SDavid du Colombier wwwauthenticate(HttpState *hs, char *line)
1119dfc0cb2SDavid du Colombier {
1129dfc0cb2SDavid du Colombier 	char cred[64], *user, *pass, *realm, *s, *spec, *name;
1139dfc0cb2SDavid du Colombier 	Fmt fmt;
1149dfc0cb2SDavid du Colombier 	UserPasswd *up;
1159dfc0cb2SDavid du Colombier 
1169dfc0cb2SDavid du Colombier 	spec = nil;
1179dfc0cb2SDavid du Colombier 	up = nil;
1189dfc0cb2SDavid du Colombier 	cred[0] = 0;
1199dfc0cb2SDavid du Colombier 	hs->autherror[0] = 0;
1209dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "basic ", 6) != 0){
1219dfc0cb2SDavid du Colombier 		werrstr("unknown auth: %s", line);
1229dfc0cb2SDavid du Colombier 		goto error;
1239dfc0cb2SDavid du Colombier 	}
1249dfc0cb2SDavid du Colombier 	line += 6;
1259dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "realm=", 6) != 0){
1269dfc0cb2SDavid du Colombier 		werrstr("missing realm: %s", line);
1279dfc0cb2SDavid du Colombier 		goto error;
1289dfc0cb2SDavid du Colombier 	}
1299dfc0cb2SDavid du Colombier 	line += 6;
1309dfc0cb2SDavid du Colombier 	user = hs->c->url->user;
1319dfc0cb2SDavid du Colombier 	pass = hs->c->url->passwd;
1329dfc0cb2SDavid du Colombier 	if(user==nil || pass==nil){
1339dfc0cb2SDavid du Colombier 		realm = unquote(line, &line);
1349dfc0cb2SDavid du Colombier 		fmtstrinit(&fmt);
1359dfc0cb2SDavid du Colombier 		name = servername(hs->netaddr);
1369dfc0cb2SDavid du Colombier 		fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm);
1379dfc0cb2SDavid du Colombier 		free(name);
1389dfc0cb2SDavid du Colombier 		if(hs->c->url->user)
1399dfc0cb2SDavid du Colombier 			fmtprint(&fmt, " user=%q", hs->c->url->user);
1409dfc0cb2SDavid du Colombier 		spec = fmtstrflush(&fmt);
1419dfc0cb2SDavid du Colombier 		if(spec == nil)
1429dfc0cb2SDavid du Colombier 			goto error;
1439dfc0cb2SDavid du Colombier 		if((up = auth_getuserpasswd(nil, "%s", spec)) == nil)
1449dfc0cb2SDavid du Colombier 			goto error;
1459dfc0cb2SDavid du Colombier 		user = up->user;
1469dfc0cb2SDavid du Colombier 		pass = up->passwd;
1479dfc0cb2SDavid du Colombier 	}
1489dfc0cb2SDavid du Colombier 	if((s = smprint("%s:%s", user, pass)) == nil)
1499dfc0cb2SDavid du Colombier 		goto error;
1509dfc0cb2SDavid du Colombier 	free(up);
1519dfc0cb2SDavid du Colombier 	enc64(cred, sizeof(cred), (uchar*)s, strlen(s));
1529dfc0cb2SDavid du Colombier 	memset(s, 0, strlen(s));
1539dfc0cb2SDavid du Colombier 	free(s);
1549dfc0cb2SDavid du Colombier 	hs->credentials = smprint("Basic %s", cred);
1559dfc0cb2SDavid du Colombier 	if(hs->credentials == nil)
1569dfc0cb2SDavid du Colombier 		goto error;
1579dfc0cb2SDavid du Colombier 	return;
1589dfc0cb2SDavid du Colombier 
1599dfc0cb2SDavid du Colombier error:
1609dfc0cb2SDavid du Colombier 	free(up);
1619dfc0cb2SDavid du Colombier 	free(spec);
1629dfc0cb2SDavid du Colombier 	snprint(hs->autherror, sizeof hs->autherror, "%r");
1639dfc0cb2SDavid du Colombier }
1649dfc0cb2SDavid du Colombier 
1659a747e4fSDavid du Colombier struct {
1669a747e4fSDavid du Colombier 	char *name;									/* Case-insensitive */
1679a747e4fSDavid du Colombier 	void (*fn)(HttpState *hs, char *value);
1689a747e4fSDavid du Colombier } hdrtab[] = {
1699a747e4fSDavid du Colombier 	{ "location:", location },
1709a747e4fSDavid du Colombier 	{ "content-type:", contenttype },
1719a747e4fSDavid du Colombier 	{ "set-cookie:", setcookie },
1729dfc0cb2SDavid du Colombier 	{ "www-authenticate:", wwwauthenticate },
1739a747e4fSDavid du Colombier };
1749a747e4fSDavid du Colombier 
1759a747e4fSDavid du Colombier static int
1769a747e4fSDavid du Colombier httprcode(HttpState *hs)
1779a747e4fSDavid du Colombier {
1789a747e4fSDavid du Colombier 	int n;
1799a747e4fSDavid du Colombier 	char *p;
1809a747e4fSDavid du Colombier 	char buf[256];
1819a747e4fSDavid du Colombier 
1829a747e4fSDavid du Colombier 	n = readline(&hs->b, buf, sizeof(buf)-1);
1839a747e4fSDavid du Colombier 	if(n <= 0)
1849a747e4fSDavid du Colombier 		return n;
1859a747e4fSDavid du Colombier 	if(httpdebug)
1869a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
1879a747e4fSDavid du Colombier 	p = strchr(buf, ' ');
1889a747e4fSDavid du Colombier 	if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){
1899a747e4fSDavid du Colombier 		werrstr("bad response from server");
1909a747e4fSDavid du Colombier 		return -1;
1919a747e4fSDavid du Colombier 	}
1929a747e4fSDavid du Colombier 	buf[n] = 0;
1939a747e4fSDavid du Colombier 	return atoi(p+1);
1949a747e4fSDavid du Colombier }
1959a747e4fSDavid du Colombier 
1969a747e4fSDavid du Colombier /*
1979a747e4fSDavid du Colombier  *  read a single mime header, collect continuations.
1989a747e4fSDavid du Colombier  *
1999a747e4fSDavid du Colombier  *  this routine assumes that there is a blank line twixt
2009a747e4fSDavid du Colombier  *  the header and the message body, otherwise bytes will
2019a747e4fSDavid du Colombier  *  be lost.
2029a747e4fSDavid du Colombier  */
2039a747e4fSDavid du Colombier static int
2049a747e4fSDavid du Colombier getheader(HttpState *hs, char *buf, int n)
2059a747e4fSDavid du Colombier {
2069a747e4fSDavid du Colombier 	char *p, *e;
2079a747e4fSDavid du Colombier 	int i;
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier 	n--;
2109a747e4fSDavid du Colombier 	p = buf;
2119a747e4fSDavid du Colombier 	for(e = p + n; ; p += i){
2129a747e4fSDavid du Colombier 		i = readline(&hs->b, p, e-p);
2139a747e4fSDavid du Colombier 		if(i < 0)
2149a747e4fSDavid du Colombier 			return i;
2159a747e4fSDavid du Colombier 
2169a747e4fSDavid du Colombier 		if(p == buf){
2179a747e4fSDavid du Colombier 			/* first line */
2189a747e4fSDavid du Colombier 			if(strchr(buf, ':') == nil)
2199a747e4fSDavid du Colombier 				break;		/* end of headers */
2209a747e4fSDavid du Colombier 		} else {
2219a747e4fSDavid du Colombier 			/* continuation line */
2229a747e4fSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
2239a747e4fSDavid du Colombier 				unreadline(&hs->b, p);
2249a747e4fSDavid du Colombier 				*p = 0;
2259a747e4fSDavid du Colombier 				break;		/* end of this header */
2269a747e4fSDavid du Colombier 			}
2279a747e4fSDavid du Colombier 		}
2289a747e4fSDavid du Colombier 	}
2299a747e4fSDavid du Colombier 
2309a747e4fSDavid du Colombier 	if(httpdebug)
2319a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
2329a747e4fSDavid du Colombier 	return p-buf;
2339a747e4fSDavid du Colombier }
2349a747e4fSDavid du Colombier 
2359a747e4fSDavid du Colombier static int
2369a747e4fSDavid du Colombier httpheaders(HttpState *hs)
2379a747e4fSDavid du Colombier {
2389a747e4fSDavid du Colombier 	char buf[2048];
2399a747e4fSDavid du Colombier 	char *p;
2409a747e4fSDavid du Colombier 	int i, n;
2419a747e4fSDavid du Colombier 
2429a747e4fSDavid du Colombier 	for(;;){
2439a747e4fSDavid du Colombier 		n = getheader(hs, buf, sizeof(buf));
2449a747e4fSDavid du Colombier 		if(n < 0)
2459a747e4fSDavid du Colombier 			return -1;
2469a747e4fSDavid du Colombier 		if(n == 0)
2479a747e4fSDavid du Colombier 			return 0;
2489a747e4fSDavid du Colombier 		//	print("http header: '%.*s'\n", n, buf);
2499a747e4fSDavid du Colombier 		for(i = 0; i < nelem(hdrtab); i++){
2509a747e4fSDavid du Colombier 			n = strlen(hdrtab[i].name);
2519a747e4fSDavid du Colombier 			if(cistrncmp(buf, hdrtab[i].name, n) == 0){
2529a747e4fSDavid du Colombier 				/* skip field name and leading white */
2539a747e4fSDavid du Colombier 				p = buf + n;
2549a747e4fSDavid du Colombier 				while(*p == ' ' || *p == '\t')
2559a747e4fSDavid du Colombier 					p++;
2569a747e4fSDavid du Colombier 				(*hdrtab[i].fn)(hs, p);
2579a747e4fSDavid du Colombier 				break;
2589a747e4fSDavid du Colombier 			}
2599a747e4fSDavid du Colombier 		}
2609a747e4fSDavid du Colombier 	}
2619a747e4fSDavid du Colombier }
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier int
2649a747e4fSDavid du Colombier httpopen(Client *c, Url *url)
2659a747e4fSDavid du Colombier {
2669dfc0cb2SDavid du Colombier 	int fd, code, redirect, authenticate;
2679a747e4fSDavid du Colombier 	char *cookies;
2689a747e4fSDavid du Colombier 	Ioproc *io;
2699a747e4fSDavid du Colombier 	HttpState *hs;
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier 	if(httpdebug)
2729a747e4fSDavid du Colombier 		fprint(2, "httpopen\n");
2739a747e4fSDavid du Colombier 	io = c->io;
2749a747e4fSDavid du Colombier 	hs = emalloc(sizeof(*hs));
2759a747e4fSDavid du Colombier 	hs->c = c;
2769a747e4fSDavid du Colombier 	hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme));
2779a747e4fSDavid du Colombier 	c->aux = hs;
2789a747e4fSDavid du Colombier 	if(httpdebug)
2799a747e4fSDavid du Colombier 		fprint(2, "dial %s\n", hs->netaddr);
2803ff48bf5SDavid du Colombier 	fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps);
2819a747e4fSDavid du Colombier 	if(fd < 0){
2829a747e4fSDavid du Colombier 	Error:
2839a747e4fSDavid du Colombier 		if(httpdebug)
2843ff48bf5SDavid du Colombier 			fprint(2, "iodial: %r\n");
2859a747e4fSDavid du Colombier 		free(hs->netaddr);
2869a747e4fSDavid du Colombier 		close(hs->fd);
2879a747e4fSDavid du Colombier 		hs->fd = -1;
288e288d156SDavid du Colombier 		free(hs);
2899a747e4fSDavid du Colombier 		c->aux = nil;
2909a747e4fSDavid du Colombier 		return -1;
2919a747e4fSDavid du Colombier 	}
2929a747e4fSDavid du Colombier 	hs->fd = fd;
2939a747e4fSDavid du Colombier 	if(httpdebug)
2949a747e4fSDavid du Colombier 		fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n",
2959a747e4fSDavid du Colombier 			c->havepostbody? "POST": " GET", url->http.page_spec, url->host);
2963ff48bf5SDavid du Colombier 	ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n",
2979a747e4fSDavid du Colombier 		c->havepostbody? "POST" : "GET", url->http.page_spec, url->host);
2989a747e4fSDavid du Colombier 	if(httpdebug)
2999a747e4fSDavid du Colombier 		fprint(2, "<- User-Agent: %s\n", c->ctl.useragent);
3009a747e4fSDavid du Colombier 	if(c->ctl.useragent)
3013ff48bf5SDavid du Colombier 		ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent);
3029a747e4fSDavid du Colombier 	if(c->ctl.sendcookies){
3039a747e4fSDavid du Colombier 		/* should we use url->page here?  sometimes it is nil. */
3049a747e4fSDavid du Colombier 		cookies = httpcookies(url->host, url->http.page_spec, 0);
3059a747e4fSDavid du Colombier 		if(cookies && cookies[0])
3063ff48bf5SDavid du Colombier 			ioprint(io, fd, "%s", cookies);
3079a747e4fSDavid du Colombier 		if(httpdebug)
3089a747e4fSDavid du Colombier 			fprint(2, "<- %s", cookies);
3099a747e4fSDavid du Colombier 		free(cookies);
3109a747e4fSDavid du Colombier 	}
3119a747e4fSDavid du Colombier 	if(c->havepostbody){
3123ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-type: %s\r\n", PostContentType);
3133ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody);
3149a747e4fSDavid du Colombier 		if(httpdebug){
3159a747e4fSDavid du Colombier 			fprint(2, "<- Content-type: %s\n", PostContentType);
3169a747e4fSDavid du Colombier 			fprint(2, "<- Content-length: %ud\n", c->npostbody);
3179a747e4fSDavid du Colombier 		}
3189a747e4fSDavid du Colombier 	}
3199dfc0cb2SDavid du Colombier 	if(c->authenticate){
3209dfc0cb2SDavid du Colombier 		ioprint(io, fd, "Authorization: %s\r\n", c->authenticate);
3219dfc0cb2SDavid du Colombier 		if(httpdebug)
3229dfc0cb2SDavid du Colombier 			fprint(2, "<- Authorization: %s\n", c->authenticate);
3239dfc0cb2SDavid du Colombier 	}
3243ff48bf5SDavid du Colombier 	ioprint(io, fd, "\r\n");
3259a747e4fSDavid du Colombier 	if(c->havepostbody)
3263ff48bf5SDavid du Colombier 		if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody)
3279a747e4fSDavid du Colombier 			goto Error;
3289a747e4fSDavid du Colombier 
3299a747e4fSDavid du Colombier 	c->havepostbody = 0;
3309a747e4fSDavid du Colombier 	redirect = 0;
3319dfc0cb2SDavid du Colombier 	authenticate = 0;
3329a747e4fSDavid du Colombier 	initibuf(&hs->b, io, fd);
3339a747e4fSDavid du Colombier 	code = httprcode(hs);
3349a747e4fSDavid du Colombier 
3359a747e4fSDavid du Colombier 	switch(code){
3369a747e4fSDavid du Colombier 	case -1:	/* connection timed out */
3379a747e4fSDavid du Colombier 		goto Error;
3389a747e4fSDavid du Colombier 
3399a747e4fSDavid du Colombier /*
3409a747e4fSDavid du Colombier 	case Eof:
3419a747e4fSDavid du Colombier 		werrstr("EOF from HTTP server");
3429a747e4fSDavid du Colombier 		goto Error;
3439a747e4fSDavid du Colombier */
3449a747e4fSDavid du Colombier 
3459a747e4fSDavid du Colombier 	case 200:	/* OK */
3469a747e4fSDavid du Colombier 	case 201:	/* Created */
3479a747e4fSDavid du Colombier 	case 202:	/* Accepted */
3489a747e4fSDavid du Colombier 	case 204:	/* No Content */
3499a747e4fSDavid du Colombier #ifdef NOT_DEFINED
3509a747e4fSDavid du Colombier 		if(ofile == nil && r->start != 0)
3519a747e4fSDavid du Colombier 			sysfatal("page changed underfoot");
3529a747e4fSDavid du Colombier #endif
3539a747e4fSDavid du Colombier 		break;
3549a747e4fSDavid du Colombier 
3559a747e4fSDavid du Colombier 	case 206:	/* Partial Content */
3569a747e4fSDavid du Colombier 		werrstr("Partial Content (206)");
3579a747e4fSDavid du Colombier 		goto Error;
3589a747e4fSDavid du Colombier 
3599a747e4fSDavid du Colombier 	case 301:	/* Moved Permanently */
3609a747e4fSDavid du Colombier 	case 302:	/* Moved Temporarily */
3619a747e4fSDavid du Colombier 		redirect = 1;
3629a747e4fSDavid du Colombier 		break;
3639a747e4fSDavid du Colombier 
3649a747e4fSDavid du Colombier 	case 304:	/* Not Modified */
3659a747e4fSDavid du Colombier 		break;
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier 	case 400:	/* Bad Request */
3689a747e4fSDavid du Colombier 		werrstr("Bad Request (400)");
3699a747e4fSDavid du Colombier 		goto Error;
3709a747e4fSDavid du Colombier 
3719a747e4fSDavid du Colombier 	case 401:	/* Unauthorized */
3729dfc0cb2SDavid du Colombier 		if(c->authenticate){
3739dfc0cb2SDavid du Colombier 			werrstr("Authentication failed (401)");
3749dfc0cb2SDavid du Colombier 			goto Error;
3759dfc0cb2SDavid du Colombier 		}
3769dfc0cb2SDavid du Colombier 		authenticate = 1;
3779dfc0cb2SDavid du Colombier 		break;
3789a747e4fSDavid du Colombier 	case 402:	/* ??? */
3799dfc0cb2SDavid du Colombier 		werrstr("Unauthorized (402)");
3809a747e4fSDavid du Colombier 		goto Error;
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier 	case 403:	/* Forbidden */
3839a747e4fSDavid du Colombier 		werrstr("Forbidden by server (403)");
3849a747e4fSDavid du Colombier 		goto Error;
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 	case 404:	/* Not Found */
3879a747e4fSDavid du Colombier 		werrstr("Not found on server (404)");
3889a747e4fSDavid du Colombier 		goto Error;
3899a747e4fSDavid du Colombier 
39035393782SDavid du Colombier 	case 405:	/* Method Not Allowed  */
39135393782SDavid du Colombier 		werrstr("Method not allowed (405)");
39235393782SDavid du Colombier 		goto Error;
39335393782SDavid du Colombier 
3949dfc0cb2SDavid du Colombier 	case 407:	/* Proxy auth */
3959dfc0cb2SDavid du Colombier 		werrstr("Proxy authentication required (407)");
3969dfc0cb2SDavid du Colombier 		goto Error;
3979dfc0cb2SDavid du Colombier 
3989a747e4fSDavid du Colombier 	case 500:	/* Internal server error */
3999a747e4fSDavid du Colombier 		werrstr("Server choked (500)");
4009a747e4fSDavid du Colombier 		goto Error;
4019a747e4fSDavid du Colombier 
4029a747e4fSDavid du Colombier 	case 501:	/* Not implemented */
4039a747e4fSDavid du Colombier 		werrstr("Server can't do it (501)");
4049a747e4fSDavid du Colombier 		goto Error;
4059a747e4fSDavid du Colombier 
4069a747e4fSDavid du Colombier 	case 502:	/* Bad gateway */
4079a747e4fSDavid du Colombier 		werrstr("Bad gateway (502)");
4089a747e4fSDavid du Colombier 		goto Error;
4099a747e4fSDavid du Colombier 
4109a747e4fSDavid du Colombier 	case 503:	/* Service unavailable */
4119a747e4fSDavid du Colombier 		werrstr("Service unavailable (503)");
4129a747e4fSDavid du Colombier 		goto Error;
4139a747e4fSDavid du Colombier 
4149a747e4fSDavid du Colombier 	default:
4159a747e4fSDavid du Colombier 		/* Bogus: we should treat unknown code XYZ as code X00 */
4169a747e4fSDavid du Colombier 		werrstr("Unknown response code %d", code);
4179a747e4fSDavid du Colombier 		goto Error;
4189a747e4fSDavid du Colombier 	}
4199a747e4fSDavid du Colombier 
4209a747e4fSDavid du Colombier 	if(httpheaders(hs) < 0)
4219a747e4fSDavid du Colombier 		goto Error;
4229a747e4fSDavid du Colombier 	if(c->ctl.acceptcookies && hs->setcookie)
4239a747e4fSDavid du Colombier 		httpsetcookie(hs->setcookie, url->host, url->path);
4249dfc0cb2SDavid du Colombier 	if(authenticate){
4259dfc0cb2SDavid du Colombier 		if(!hs->credentials){
4269dfc0cb2SDavid du Colombier 			if(hs->autherror[0])
4279dfc0cb2SDavid du Colombier 				werrstr("%s", hs->autherror);
4289dfc0cb2SDavid du Colombier 			else
4299dfc0cb2SDavid du Colombier 				werrstr("unauthorized; no www-authenticate: header");
4309dfc0cb2SDavid du Colombier 			return -1;
4319dfc0cb2SDavid du Colombier 		}
4329dfc0cb2SDavid du Colombier 		c->authenticate = hs->credentials;
4339dfc0cb2SDavid du Colombier 		hs->credentials = nil;
4349dfc0cb2SDavid du Colombier 	}
4359a747e4fSDavid du Colombier 	if(redirect){
4369a747e4fSDavid du Colombier 		if(!hs->location){
4379a747e4fSDavid du Colombier 			werrstr("redirection without Location: header");
4389a747e4fSDavid du Colombier 			return -1;
4399a747e4fSDavid du Colombier 		}
4409a747e4fSDavid du Colombier 		c->redirect = hs->location;
4419a747e4fSDavid du Colombier 		hs->location = nil;
4429a747e4fSDavid du Colombier 	}
4439a747e4fSDavid du Colombier 	return 0;
4449a747e4fSDavid du Colombier }
4459a747e4fSDavid du Colombier 
4469a747e4fSDavid du Colombier int
4479a747e4fSDavid du Colombier httpread(Client *c, Req *r)
4489a747e4fSDavid du Colombier {
4499a747e4fSDavid du Colombier 	HttpState *hs;
450*1066d6deSDavid du Colombier 	long n;
4519a747e4fSDavid du Colombier 
4529a747e4fSDavid du Colombier 	hs = c->aux;
453*1066d6deSDavid du Colombier 	n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count);
454*1066d6deSDavid du Colombier 	if(n < 0)
4559a747e4fSDavid du Colombier 		return -1;
456*1066d6deSDavid du Colombier 
457*1066d6deSDavid du Colombier 	r->ofcall.count = n;
4589a747e4fSDavid du Colombier 	return 0;
4599a747e4fSDavid du Colombier }
4609a747e4fSDavid du Colombier 
4619a747e4fSDavid du Colombier void
4629a747e4fSDavid du Colombier httpclose(Client *c)
4639a747e4fSDavid du Colombier {
4649a747e4fSDavid du Colombier 	HttpState *hs;
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier 	hs = c->aux;
4676b6b9ac8SDavid du Colombier 	if(hs == nil)
4686b6b9ac8SDavid du Colombier 		return;
4693ff48bf5SDavid du Colombier 	ioclose(c->io, hs->fd);
4709a747e4fSDavid du Colombier 	hs->fd = -1;
4719a747e4fSDavid du Colombier 	free(hs->location);
4729a747e4fSDavid du Colombier 	free(hs->setcookie);
4739a747e4fSDavid du Colombier 	free(hs->netaddr);
4749dfc0cb2SDavid du Colombier 	free(hs->credentials);
4759a747e4fSDavid du Colombier 	free(hs);
4769a747e4fSDavid du Colombier 	c->aux = nil;
4779a747e4fSDavid du Colombier }
4789a747e4fSDavid du Colombier 
479