xref: /plan9/sys/src/cmd/webfs/http.c (revision b39189fd423aed869c5cf5189bc504918cff969b)
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 
141066d6deSDavid 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
location(HttpState * hs,char * value)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
contenttype(HttpState * hs,char * value)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
setcookie(HttpState * hs,char * value)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*
unquote(char * s,char ** ps)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*
servername(char * addr)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
wwwauthenticate(HttpState * hs,char * line)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");
163ac84fd08SDavid du Colombier 	fprint(2, "%s: Authentication failed: %r\n", argv0);
1649dfc0cb2SDavid du Colombier }
1659dfc0cb2SDavid du Colombier 
1669a747e4fSDavid du Colombier struct {
1679a747e4fSDavid du Colombier 	char *name;									/* Case-insensitive */
1689a747e4fSDavid du Colombier 	void (*fn)(HttpState *hs, char *value);
1699a747e4fSDavid du Colombier } hdrtab[] = {
1709a747e4fSDavid du Colombier 	{ "location:", location },
1719a747e4fSDavid du Colombier 	{ "content-type:", contenttype },
1729a747e4fSDavid du Colombier 	{ "set-cookie:", setcookie },
1739dfc0cb2SDavid du Colombier 	{ "www-authenticate:", wwwauthenticate },
1749a747e4fSDavid du Colombier };
1759a747e4fSDavid du Colombier 
1769a747e4fSDavid du Colombier static int
httprcode(HttpState * hs)1779a747e4fSDavid du Colombier httprcode(HttpState *hs)
1789a747e4fSDavid du Colombier {
1799a747e4fSDavid du Colombier 	int n;
1809a747e4fSDavid du Colombier 	char *p;
1819a747e4fSDavid du Colombier 	char buf[256];
1829a747e4fSDavid du Colombier 
1839a747e4fSDavid du Colombier 	n = readline(&hs->b, buf, sizeof(buf)-1);
1849a747e4fSDavid du Colombier 	if(n <= 0)
1859a747e4fSDavid du Colombier 		return n;
1869a747e4fSDavid du Colombier 	if(httpdebug)
1879a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
1889a747e4fSDavid du Colombier 	p = strchr(buf, ' ');
1899a747e4fSDavid du Colombier 	if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){
1909a747e4fSDavid du Colombier 		werrstr("bad response from server");
1919a747e4fSDavid du Colombier 		return -1;
1929a747e4fSDavid du Colombier 	}
1939a747e4fSDavid du Colombier 	buf[n] = 0;
1949a747e4fSDavid du Colombier 	return atoi(p+1);
1959a747e4fSDavid du Colombier }
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier /*
1989a747e4fSDavid du Colombier  *  read a single mime header, collect continuations.
1999a747e4fSDavid du Colombier  *
2009a747e4fSDavid du Colombier  *  this routine assumes that there is a blank line twixt
2019a747e4fSDavid du Colombier  *  the header and the message body, otherwise bytes will
2029a747e4fSDavid du Colombier  *  be lost.
2039a747e4fSDavid du Colombier  */
2049a747e4fSDavid du Colombier static int
getheader(HttpState * hs,char * buf,int n)2059a747e4fSDavid du Colombier getheader(HttpState *hs, char *buf, int n)
2069a747e4fSDavid du Colombier {
2079a747e4fSDavid du Colombier 	char *p, *e;
2089a747e4fSDavid du Colombier 	int i;
2099a747e4fSDavid du Colombier 
2109a747e4fSDavid du Colombier 	n--;
2119a747e4fSDavid du Colombier 	p = buf;
2129a747e4fSDavid du Colombier 	for(e = p + n; ; p += i){
2139a747e4fSDavid du Colombier 		i = readline(&hs->b, p, e-p);
2149a747e4fSDavid du Colombier 		if(i < 0)
2159a747e4fSDavid du Colombier 			return i;
2169a747e4fSDavid du Colombier 
2179a747e4fSDavid du Colombier 		if(p == buf){
2189a747e4fSDavid du Colombier 			/* first line */
2199a747e4fSDavid du Colombier 			if(strchr(buf, ':') == nil)
2209a747e4fSDavid du Colombier 				break;		/* end of headers */
2219a747e4fSDavid du Colombier 		} else {
2229a747e4fSDavid du Colombier 			/* continuation line */
2239a747e4fSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
2249a747e4fSDavid du Colombier 				unreadline(&hs->b, p);
2259a747e4fSDavid du Colombier 				*p = 0;
2269a747e4fSDavid du Colombier 				break;		/* end of this header */
2279a747e4fSDavid du Colombier 			}
2289a747e4fSDavid du Colombier 		}
2299a747e4fSDavid du Colombier 	}
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier 	if(httpdebug)
2329a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
2339a747e4fSDavid du Colombier 	return p-buf;
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier 
2369a747e4fSDavid du Colombier static int
httpheaders(HttpState * hs)2379a747e4fSDavid du Colombier httpheaders(HttpState *hs)
2389a747e4fSDavid du Colombier {
2399a747e4fSDavid du Colombier 	char buf[2048];
2409a747e4fSDavid du Colombier 	char *p;
2419a747e4fSDavid du Colombier 	int i, n;
2429a747e4fSDavid du Colombier 
2439a747e4fSDavid du Colombier 	for(;;){
2449a747e4fSDavid du Colombier 		n = getheader(hs, buf, sizeof(buf));
2459a747e4fSDavid du Colombier 		if(n < 0)
2469a747e4fSDavid du Colombier 			return -1;
2479a747e4fSDavid du Colombier 		if(n == 0)
2489a747e4fSDavid du Colombier 			return 0;
2499a747e4fSDavid du Colombier 		//	print("http header: '%.*s'\n", n, buf);
2509a747e4fSDavid du Colombier 		for(i = 0; i < nelem(hdrtab); i++){
2519a747e4fSDavid du Colombier 			n = strlen(hdrtab[i].name);
2529a747e4fSDavid du Colombier 			if(cistrncmp(buf, hdrtab[i].name, n) == 0){
2539a747e4fSDavid du Colombier 				/* skip field name and leading white */
2549a747e4fSDavid du Colombier 				p = buf + n;
2559a747e4fSDavid du Colombier 				while(*p == ' ' || *p == '\t')
2569a747e4fSDavid du Colombier 					p++;
2579a747e4fSDavid du Colombier 				(*hdrtab[i].fn)(hs, p);
2589a747e4fSDavid du Colombier 				break;
2599a747e4fSDavid du Colombier 			}
2609a747e4fSDavid du Colombier 		}
2619a747e4fSDavid du Colombier 	}
2629a747e4fSDavid du Colombier }
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier int
httpopen(Client * c,Url * url)2659a747e4fSDavid du Colombier httpopen(Client *c, Url *url)
2669a747e4fSDavid du Colombier {
2679dfc0cb2SDavid du Colombier 	int fd, code, redirect, authenticate;
2689a747e4fSDavid du Colombier 	char *cookies;
2699a747e4fSDavid du Colombier 	Ioproc *io;
2709a747e4fSDavid du Colombier 	HttpState *hs;
271360053c8SDavid du Colombier 	char *service;
2729a747e4fSDavid du Colombier 
2739a747e4fSDavid du Colombier 	if(httpdebug)
2749a747e4fSDavid du Colombier 		fprint(2, "httpopen\n");
2759a747e4fSDavid du Colombier 	io = c->io;
2769a747e4fSDavid du Colombier 	hs = emalloc(sizeof(*hs));
2779a747e4fSDavid du Colombier 	hs->c = c;
278360053c8SDavid du Colombier 
279360053c8SDavid du Colombier 	if(url->port)
280360053c8SDavid du Colombier 		service = url->port;
281360053c8SDavid du Colombier 	else
282360053c8SDavid du Colombier 		service = url->scheme;
283360053c8SDavid du Colombier 	hs->netaddr = estrdup(netmkaddr(url->host, 0, service));
2849a747e4fSDavid du Colombier 	c->aux = hs;
285360053c8SDavid du Colombier 	if(httpdebug){
2869a747e4fSDavid du Colombier 		fprint(2, "dial %s\n", hs->netaddr);
287360053c8SDavid du Colombier 		fprint(2, "dial port: %s\n", url->port);
288360053c8SDavid du Colombier 	}
2893ff48bf5SDavid du Colombier 	fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps);
2909a747e4fSDavid du Colombier 	if(fd < 0){
2919a747e4fSDavid du Colombier 	Error:
2929a747e4fSDavid du Colombier 		if(httpdebug)
2933ff48bf5SDavid du Colombier 			fprint(2, "iodial: %r\n");
294ac84fd08SDavid du Colombier 		free(hs->location);
295ac84fd08SDavid du Colombier 		free(hs->setcookie);
2969a747e4fSDavid du Colombier 		free(hs->netaddr);
297ac84fd08SDavid du Colombier 		free(hs->credentials);
298ac84fd08SDavid du Colombier 		if(fd >= 0)
299ac84fd08SDavid du Colombier 			ioclose(io, hs->fd);
3009a747e4fSDavid du Colombier 		hs->fd = -1;
301e288d156SDavid du Colombier 		free(hs);
3029a747e4fSDavid du Colombier 		c->aux = nil;
3039a747e4fSDavid du Colombier 		return -1;
3049a747e4fSDavid du Colombier 	}
3059a747e4fSDavid du Colombier 	hs->fd = fd;
3069a747e4fSDavid du Colombier 	if(httpdebug)
3079a747e4fSDavid du Colombier 		fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n",
3089a747e4fSDavid du Colombier 			c->havepostbody? "POST": "GET", url->http.page_spec, url->host);
3093ff48bf5SDavid du Colombier 	ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n",
3109a747e4fSDavid du Colombier 		c->havepostbody? "POST" : "GET", url->http.page_spec, url->host);
3119a747e4fSDavid du Colombier 	if(httpdebug)
3129a747e4fSDavid du Colombier 		fprint(2, "<- User-Agent: %s\n", c->ctl.useragent);
3139a747e4fSDavid du Colombier 	if(c->ctl.useragent)
3143ff48bf5SDavid du Colombier 		ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent);
3159a747e4fSDavid du Colombier 	if(c->ctl.sendcookies){
3169a747e4fSDavid du Colombier 		/* should we use url->page here?  sometimes it is nil. */
3172c23bbf7SDavid du Colombier 		cookies = httpcookies(url->host, url->http.page_spec,
3182c23bbf7SDavid du Colombier 			url->ischeme == UShttps);
3199a747e4fSDavid du Colombier 		if(cookies && cookies[0])
3203ff48bf5SDavid du Colombier 			ioprint(io, fd, "%s", cookies);
3219a747e4fSDavid du Colombier 		if(httpdebug)
3229a747e4fSDavid du Colombier 			fprint(2, "<- %s", cookies);
3239a747e4fSDavid du Colombier 		free(cookies);
3249a747e4fSDavid du Colombier 	}
3259a747e4fSDavid du Colombier 	if(c->havepostbody){
3263ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-type: %s\r\n", PostContentType);
3273ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody);
3289a747e4fSDavid du Colombier 		if(httpdebug){
3299a747e4fSDavid du Colombier 			fprint(2, "<- Content-type: %s\n", PostContentType);
3309a747e4fSDavid du Colombier 			fprint(2, "<- Content-length: %ud\n", c->npostbody);
3319a747e4fSDavid du Colombier 		}
3329a747e4fSDavid du Colombier 	}
3339dfc0cb2SDavid du Colombier 	if(c->authenticate){
3349dfc0cb2SDavid du Colombier 		ioprint(io, fd, "Authorization: %s\r\n", c->authenticate);
3359dfc0cb2SDavid du Colombier 		if(httpdebug)
3369dfc0cb2SDavid du Colombier 			fprint(2, "<- Authorization: %s\n", c->authenticate);
3379dfc0cb2SDavid du Colombier 	}
3383ff48bf5SDavid du Colombier 	ioprint(io, fd, "\r\n");
3399a747e4fSDavid du Colombier 	if(c->havepostbody)
3403ff48bf5SDavid du Colombier 		if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody)
3419a747e4fSDavid du Colombier 			goto Error;
3429a747e4fSDavid du Colombier 
3439a747e4fSDavid du Colombier 	redirect = 0;
3449dfc0cb2SDavid du Colombier 	authenticate = 0;
3459a747e4fSDavid du Colombier 	initibuf(&hs->b, io, fd);
3469a747e4fSDavid du Colombier 	code = httprcode(hs);
3479a747e4fSDavid du Colombier 
3489a747e4fSDavid du Colombier 	switch(code){
3499a747e4fSDavid du Colombier 	case -1:	/* connection timed out */
3509a747e4fSDavid du Colombier 		goto Error;
3519a747e4fSDavid du Colombier 
3529a747e4fSDavid du Colombier /*
3539a747e4fSDavid du Colombier 	case Eof:
3549a747e4fSDavid du Colombier 		werrstr("EOF from HTTP server");
3559a747e4fSDavid du Colombier 		goto Error;
3569a747e4fSDavid du Colombier */
3579a747e4fSDavid du Colombier 
3589a747e4fSDavid du Colombier 	case 200:	/* OK */
3599a747e4fSDavid du Colombier 	case 201:	/* Created */
3609a747e4fSDavid du Colombier 	case 202:	/* Accepted */
3619a747e4fSDavid du Colombier 	case 204:	/* No Content */
362de8abbc9SDavid du Colombier 	case 205: /* Reset Content */
3639a747e4fSDavid du Colombier #ifdef NOT_DEFINED
3649a747e4fSDavid du Colombier 		if(ofile == nil && r->start != 0)
3659a747e4fSDavid du Colombier 			sysfatal("page changed underfoot");
3669a747e4fSDavid du Colombier #endif
3679a747e4fSDavid du Colombier 		break;
3689a747e4fSDavid du Colombier 
3699a747e4fSDavid du Colombier 	case 206:	/* Partial Content */
3709a747e4fSDavid du Colombier 		werrstr("Partial Content (206)");
3719a747e4fSDavid du Colombier 		goto Error;
3729a747e4fSDavid du Colombier 
3739a747e4fSDavid du Colombier 	case 301:	/* Moved Permanently */
3749a747e4fSDavid du Colombier 	case 302:	/* Moved Temporarily */
3757c70c028SDavid du Colombier 	case 303:	/* See Other */
376de8abbc9SDavid du Colombier 	case 307: /* Temporary Redirect  */
3779a747e4fSDavid du Colombier 		redirect = 1;
3789a747e4fSDavid du Colombier 		break;
3799a747e4fSDavid du Colombier 
3809a747e4fSDavid du Colombier 	case 304:	/* Not Modified */
3819a747e4fSDavid du Colombier 		break;
3829a747e4fSDavid du Colombier 
3839a747e4fSDavid du Colombier 	case 400:	/* Bad Request */
3849a747e4fSDavid du Colombier 		werrstr("Bad Request (400)");
3859a747e4fSDavid du Colombier 		goto Error;
3869a747e4fSDavid du Colombier 
3879a747e4fSDavid du Colombier 	case 401:	/* Unauthorized */
3889dfc0cb2SDavid du Colombier 		if(c->authenticate){
3899dfc0cb2SDavid du Colombier 			werrstr("Authentication failed (401)");
3909dfc0cb2SDavid du Colombier 			goto Error;
3919dfc0cb2SDavid du Colombier 		}
3929dfc0cb2SDavid du Colombier 		authenticate = 1;
3939dfc0cb2SDavid du Colombier 		break;
394de8abbc9SDavid du Colombier 	case 402:	/* Payment Required */
395de8abbc9SDavid du Colombier 		werrstr("Payment Required (402)");
3969a747e4fSDavid du Colombier 		goto Error;
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	case 403:	/* Forbidden */
3999a747e4fSDavid du Colombier 		werrstr("Forbidden by server (403)");
4009a747e4fSDavid du Colombier 		goto Error;
4019a747e4fSDavid du Colombier 
4029a747e4fSDavid du Colombier 	case 404:	/* Not Found */
4039a747e4fSDavid du Colombier 		werrstr("Not found on server (404)");
4049a747e4fSDavid du Colombier 		goto Error;
4059a747e4fSDavid du Colombier 
40635393782SDavid du Colombier 	case 405:	/* Method Not Allowed  */
40735393782SDavid du Colombier 		werrstr("Method not allowed (405)");
40835393782SDavid du Colombier 		goto Error;
40935393782SDavid du Colombier 
410de8abbc9SDavid du Colombier 	case 406: /* Not Acceptable */
411de8abbc9SDavid du Colombier 		werrstr("Not Acceptable (406)");
412de8abbc9SDavid du Colombier 		goto Error;
413de8abbc9SDavid du Colombier 
4149dfc0cb2SDavid du Colombier 	case 407:	/* Proxy auth */
4159dfc0cb2SDavid du Colombier 		werrstr("Proxy authentication required (407)");
4169dfc0cb2SDavid du Colombier 		goto Error;
4179dfc0cb2SDavid du Colombier 
418de8abbc9SDavid du Colombier 	case 408: /* Request Timeout */
419de8abbc9SDavid du Colombier 		werrstr("Request Timeout (408)");
420de8abbc9SDavid du Colombier 		goto Error;
421de8abbc9SDavid du Colombier 
422de8abbc9SDavid du Colombier 	case 409: /* Conflict */
423de8abbc9SDavid du Colombier 		werrstr("Conflict  (409)");
424de8abbc9SDavid du Colombier 		goto Error;
425de8abbc9SDavid du Colombier 
426de8abbc9SDavid du Colombier 	case 410: /* Gone */
427de8abbc9SDavid du Colombier 		werrstr("Gone  (410)");
428de8abbc9SDavid du Colombier 		goto Error;
429de8abbc9SDavid du Colombier 
430de8abbc9SDavid du Colombier 	case 411: /* Length Required */
431de8abbc9SDavid du Colombier 		werrstr("Length Required  (411)");
432de8abbc9SDavid du Colombier 		goto Error;
433de8abbc9SDavid du Colombier 
434de8abbc9SDavid du Colombier 	case 412: /* Precondition Failed */
435de8abbc9SDavid du Colombier 		werrstr("Precondition Failed  (412)");
436de8abbc9SDavid du Colombier 		goto Error;
437de8abbc9SDavid du Colombier 
438de8abbc9SDavid du Colombier 	case 413: /* Request Entity Too Large */
439de8abbc9SDavid du Colombier 		werrstr("Request Entity Too Large  (413)");
440de8abbc9SDavid du Colombier 		goto Error;
441de8abbc9SDavid du Colombier 
442de8abbc9SDavid du Colombier 	case 414: /* Request-URI Too Long */
443de8abbc9SDavid du Colombier 		werrstr("Request-URI Too Long  (414)");
444de8abbc9SDavid du Colombier 		goto Error;
445de8abbc9SDavid du Colombier 
446de8abbc9SDavid du Colombier 	case 415: /* Unsupported Media Type */
447de8abbc9SDavid du Colombier 		werrstr("Unsupported Media Type  (415)");
448de8abbc9SDavid du Colombier 		goto Error;
449de8abbc9SDavid du Colombier 
450de8abbc9SDavid du Colombier 	case 416: /* Requested Range Not Satisfiable */
451de8abbc9SDavid du Colombier 		werrstr("Requested Range Not Satisfiable  (416)");
452de8abbc9SDavid du Colombier 		goto Error;
453de8abbc9SDavid du Colombier 
454de8abbc9SDavid du Colombier 	case 417: /* Expectation Failed */
455de8abbc9SDavid du Colombier 		werrstr("Expectation Failed  (417)");
456de8abbc9SDavid du Colombier 		goto Error;
457de8abbc9SDavid du Colombier 
4589a747e4fSDavid du Colombier 	case 500:	/* Internal server error */
4599a747e4fSDavid du Colombier 		werrstr("Server choked (500)");
4609a747e4fSDavid du Colombier 		goto Error;
4619a747e4fSDavid du Colombier 
4629a747e4fSDavid du Colombier 	case 501:	/* Not implemented */
4639a747e4fSDavid du Colombier 		werrstr("Server can't do it (501)");
4649a747e4fSDavid du Colombier 		goto Error;
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier 	case 502:	/* Bad gateway */
4679a747e4fSDavid du Colombier 		werrstr("Bad gateway (502)");
4689a747e4fSDavid du Colombier 		goto Error;
4699a747e4fSDavid du Colombier 
4709a747e4fSDavid du Colombier 	case 503:	/* Service unavailable */
4719a747e4fSDavid du Colombier 		werrstr("Service unavailable (503)");
4729a747e4fSDavid du Colombier 		goto Error;
4739a747e4fSDavid du Colombier 
4749a747e4fSDavid du Colombier 	default:
4759a747e4fSDavid du Colombier 		/* Bogus: we should treat unknown code XYZ as code X00 */
4769a747e4fSDavid du Colombier 		werrstr("Unknown response code %d", code);
4779a747e4fSDavid du Colombier 		goto Error;
4789a747e4fSDavid du Colombier 	}
4799a747e4fSDavid du Colombier 
4809a747e4fSDavid du Colombier 	if(httpheaders(hs) < 0)
4819a747e4fSDavid du Colombier 		goto Error;
4829a747e4fSDavid du Colombier 	if(c->ctl.acceptcookies && hs->setcookie)
4839a747e4fSDavid du Colombier 		httpsetcookie(hs->setcookie, url->host, url->path);
4849dfc0cb2SDavid du Colombier 	if(authenticate){
4859dfc0cb2SDavid du Colombier 		if(!hs->credentials){
4869dfc0cb2SDavid du Colombier 			if(hs->autherror[0])
4879dfc0cb2SDavid du Colombier 				werrstr("%s", hs->autherror);
4889dfc0cb2SDavid du Colombier 			else
4899dfc0cb2SDavid du Colombier 				werrstr("unauthorized; no www-authenticate: header");
490ac84fd08SDavid du Colombier 			goto Error;
4919dfc0cb2SDavid du Colombier 		}
4929dfc0cb2SDavid du Colombier 		c->authenticate = hs->credentials;
4939dfc0cb2SDavid du Colombier 		hs->credentials = nil;
494*b39189fdSDavid du Colombier 	}else if(c->authenticate)
495*b39189fdSDavid du Colombier 		c->authenticate = 0;
4969a747e4fSDavid du Colombier 	if(redirect){
4979a747e4fSDavid du Colombier 		if(!hs->location){
4989a747e4fSDavid du Colombier 			werrstr("redirection without Location: header");
499ac84fd08SDavid du Colombier 			goto Error;
5009a747e4fSDavid du Colombier 		}
5019a747e4fSDavid du Colombier 		c->redirect = hs->location;
5029a747e4fSDavid du Colombier 		hs->location = nil;
5039a747e4fSDavid du Colombier 	}
5049a747e4fSDavid du Colombier 	return 0;
5059a747e4fSDavid du Colombier }
5069a747e4fSDavid du Colombier 
5079a747e4fSDavid du Colombier int
httpread(Client * c,Req * r)5089a747e4fSDavid du Colombier httpread(Client *c, Req *r)
5099a747e4fSDavid du Colombier {
5109a747e4fSDavid du Colombier 	HttpState *hs;
5111066d6deSDavid du Colombier 	long n;
5129a747e4fSDavid du Colombier 
5139a747e4fSDavid du Colombier 	hs = c->aux;
5141066d6deSDavid du Colombier 	n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count);
5151066d6deSDavid du Colombier 	if(n < 0)
5169a747e4fSDavid du Colombier 		return -1;
5171066d6deSDavid du Colombier 
5181066d6deSDavid du Colombier 	r->ofcall.count = n;
5199a747e4fSDavid du Colombier 	return 0;
5209a747e4fSDavid du Colombier }
5219a747e4fSDavid du Colombier 
5229a747e4fSDavid du Colombier void
httpclose(Client * c)5239a747e4fSDavid du Colombier httpclose(Client *c)
5249a747e4fSDavid du Colombier {
5259a747e4fSDavid du Colombier 	HttpState *hs;
5269a747e4fSDavid du Colombier 
5279a747e4fSDavid du Colombier 	hs = c->aux;
5286b6b9ac8SDavid du Colombier 	if(hs == nil)
5296b6b9ac8SDavid du Colombier 		return;
530ac84fd08SDavid du Colombier 	if(hs->fd >= 0)
5313ff48bf5SDavid du Colombier 		ioclose(c->io, hs->fd);
5329a747e4fSDavid du Colombier 	hs->fd = -1;
5339a747e4fSDavid du Colombier 	free(hs->location);
5349a747e4fSDavid du Colombier 	free(hs->setcookie);
5359a747e4fSDavid du Colombier 	free(hs->netaddr);
5369dfc0cb2SDavid du Colombier 	free(hs->credentials);
5379a747e4fSDavid du Colombier 	free(hs);
5389a747e4fSDavid du Colombier 	c->aux = nil;
5399a747e4fSDavid du Colombier }
540