xref: /plan9-contrib/sys/src/cmd/ip/httpd/httpd.c (revision b51d2625c40d69189e832341e18a1588449a10c1)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <auth.h>
49a747e4fSDavid du Colombier #include <mp.h>
59a747e4fSDavid du Colombier #include <libsec.h>
67dd7cddfSDavid du Colombier #include "httpd.h"
780ee5cbfSDavid du Colombier #include "httpsrv.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier typedef struct Strings		Strings;
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier struct Strings
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier 	char	*s1;
147dd7cddfSDavid du Colombier 	char	*s2;
157dd7cddfSDavid du Colombier };
167dd7cddfSDavid du Colombier 
1780ee5cbfSDavid du Colombier char	*netdir;
1880ee5cbfSDavid du Colombier char	*webroot;
1980ee5cbfSDavid du Colombier char	*HTTPLOG = "httpd/log";
207dd7cddfSDavid du Colombier 
2180ee5cbfSDavid du Colombier static	char		netdirb[256];
2280ee5cbfSDavid du Colombier static	char		*namespace;
2380ee5cbfSDavid du Colombier 
247dd7cddfSDavid du Colombier static	void		becomenone(char*);
257dd7cddfSDavid du Colombier static	char		*csquery(char*, char*, char*);
267dd7cddfSDavid du Colombier static	void		dolisten(char*);
2780ee5cbfSDavid du Colombier static	int		doreq(HConnect*);
2880ee5cbfSDavid du Colombier static	int		send(HConnect*);
2980ee5cbfSDavid du Colombier static	Strings		stripmagic(HConnect*, char*);
307dd7cddfSDavid du Colombier static	char*		stripprefix(char*, char*);
317dd7cddfSDavid du Colombier static	char*		sysdom(void);
329a747e4fSDavid du Colombier static	int		notfound(HConnect *c, char *url);
339a747e4fSDavid du Colombier 
349a747e4fSDavid du Colombier uchar *certificate;
359a747e4fSDavid du Colombier int certlen;
365ede6b93SDavid du Colombier PEMChain *certchain;
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier void
397dd7cddfSDavid du Colombier usage(void)
407dd7cddfSDavid du Colombier {
415ede6b93SDavid du Colombier 	fprint(2, "usage: httpd [-c certificate] [-C CAchain] [-a srvaddress] [-d domain] [-n namespace] [-w webroot]\n");
427dd7cddfSDavid du Colombier 	exits("usage");
437dd7cddfSDavid du Colombier }
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier void
467dd7cddfSDavid du Colombier main(int argc, char **argv)
477dd7cddfSDavid du Colombier {
487dd7cddfSDavid du Colombier 	char *address;
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier 	namespace = nil;
517dd7cddfSDavid du Colombier 	address = nil;
5280ee5cbfSDavid du Colombier 	hmydomain = nil;
5380ee5cbfSDavid du Colombier 	netdir = "/net";
549a747e4fSDavid du Colombier 	fmtinstall('D', hdatefmt);
559a747e4fSDavid du Colombier 	fmtinstall('H', httpfmt);
569a747e4fSDavid du Colombier 	fmtinstall('U', hurlfmt);
577dd7cddfSDavid du Colombier 	ARGBEGIN{
589a747e4fSDavid du Colombier 	case 'c':
599a747e4fSDavid du Colombier 		certificate = readcert(ARGF(), &certlen);
609a747e4fSDavid du Colombier 		if(certificate == nil)
61d9306527SDavid du Colombier 			sysfatal("reading certificate: %r");
629a747e4fSDavid du Colombier 		break;
635ede6b93SDavid du Colombier 	case 'C':
645ede6b93SDavid du Colombier 		certchain = readcertchain(ARGF());
655ede6b93SDavid du Colombier 		if (certchain == nil)
665ede6b93SDavid du Colombier 			sysfatal("reading certificate chain: %r");
675ede6b93SDavid du Colombier 		break;
687dd7cddfSDavid du Colombier 	case 'n':
697dd7cddfSDavid du Colombier 		namespace = ARGF();
707dd7cddfSDavid du Colombier 		break;
717dd7cddfSDavid du Colombier 	case 'a':
727dd7cddfSDavid du Colombier 		address = ARGF();
737dd7cddfSDavid du Colombier 		break;
747dd7cddfSDavid du Colombier 	case 'd':
7580ee5cbfSDavid du Colombier 		hmydomain = ARGF();
767dd7cddfSDavid du Colombier 		break;
777dd7cddfSDavid du Colombier 	case 'w':
787dd7cddfSDavid du Colombier 		webroot = ARGF();
797dd7cddfSDavid du Colombier 		break;
807dd7cddfSDavid du Colombier 	default:
817dd7cddfSDavid du Colombier 		usage();
827dd7cddfSDavid du Colombier 		break;
837dd7cddfSDavid du Colombier 	}ARGEND
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier 	if(argc)
867dd7cddfSDavid du Colombier 		usage();
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier 	if(namespace == nil)
897dd7cddfSDavid du Colombier 		namespace = "/lib/namespace.httpd";
907dd7cddfSDavid du Colombier 	if(address == nil)
919a747e4fSDavid du Colombier 		address = "*";
927dd7cddfSDavid du Colombier 	if(webroot == nil)
937dd7cddfSDavid du Colombier 		webroot = "/usr/web";
947dd7cddfSDavid du Colombier 	else{
957dd7cddfSDavid du Colombier 		cleanname(webroot);
967dd7cddfSDavid du Colombier 		if(webroot[0] != '/')
977dd7cddfSDavid du Colombier 			webroot = "/usr/web";
987dd7cddfSDavid du Colombier 	}
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 	switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
1017dd7cddfSDavid du Colombier 	case -1:
1027dd7cddfSDavid du Colombier 		sysfatal("fork");
1037dd7cddfSDavid du Colombier 	case 0:
1047dd7cddfSDavid du Colombier 		break;
1057dd7cddfSDavid du Colombier 	default:
1067dd7cddfSDavid du Colombier 		exits(nil);
1077dd7cddfSDavid du Colombier 	}
1087dd7cddfSDavid du Colombier 
1097dd7cddfSDavid du Colombier 	/*
1107dd7cddfSDavid du Colombier 	 * open all files we might need before castrating namespace
1117dd7cddfSDavid du Colombier 	 */
1127dd7cddfSDavid du Colombier 	time(nil);
11380ee5cbfSDavid du Colombier 	if(hmydomain == nil)
11480ee5cbfSDavid du Colombier 		hmydomain = sysdom();
1157dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, nil);
1167dd7cddfSDavid du Colombier 	logall[0] = open("/sys/log/httpd/0", OWRITE);
1177dd7cddfSDavid du Colombier 	logall[1] = open("/sys/log/httpd/1", OWRITE);
118499069deSDavid du Colombier 	logall[2] = open("/sys/log/httpd/clf", OWRITE);
1197dd7cddfSDavid du Colombier 	redirectinit();
1207dd7cddfSDavid du Colombier 	contentinit();
1217dd7cddfSDavid du Colombier 	urlinit();
1227dd7cddfSDavid du Colombier 	statsinit();
1237dd7cddfSDavid du Colombier 
1247dd7cddfSDavid du Colombier 	becomenone(namespace);
1259a747e4fSDavid du Colombier 	dolisten(netmkaddr(address, "tcp", certificate == nil ? "http" : "https"));
1267dd7cddfSDavid du Colombier 	exits(nil);
1277dd7cddfSDavid du Colombier }
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier static void
1307dd7cddfSDavid du Colombier becomenone(char *namespace)
1317dd7cddfSDavid du Colombier {
1327dd7cddfSDavid du Colombier 	int fd;
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	fd = open("#c/user", OWRITE);
1357dd7cddfSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
1367dd7cddfSDavid du Colombier 		sysfatal("can't become none");
1377dd7cddfSDavid du Colombier 	close(fd);
1387dd7cddfSDavid du Colombier 	if(newns("none", nil) < 0)
1397dd7cddfSDavid du Colombier 		sysfatal("can't build normal namespace");
1407dd7cddfSDavid du Colombier 	if(addns("none", namespace) < 0)
1417dd7cddfSDavid du Colombier 		sysfatal("can't build httpd namespace");
1427dd7cddfSDavid du Colombier }
1437dd7cddfSDavid du Colombier 
14480ee5cbfSDavid du Colombier static HConnect*
1457dd7cddfSDavid du Colombier mkconnect(void)
1467dd7cddfSDavid du Colombier {
14780ee5cbfSDavid du Colombier 	HConnect *c;
1487dd7cddfSDavid du Colombier 
14980ee5cbfSDavid du Colombier 	c = ezalloc(sizeof(HConnect));
1507dd7cddfSDavid du Colombier 	c->hpos = c->header;
1517dd7cddfSDavid du Colombier 	c->hstop = c->header;
15280ee5cbfSDavid du Colombier 	c->replog = writelog;
1537dd7cddfSDavid du Colombier 	return c;
1547dd7cddfSDavid du Colombier }
1557dd7cddfSDavid du Colombier 
15680ee5cbfSDavid du Colombier static HSPriv*
15780ee5cbfSDavid du Colombier mkhspriv(void)
15880ee5cbfSDavid du Colombier {
15980ee5cbfSDavid du Colombier 	HSPriv *p;
16080ee5cbfSDavid du Colombier 
16180ee5cbfSDavid du Colombier 	p = ezalloc(sizeof(HSPriv));
16280ee5cbfSDavid du Colombier 	return p;
16380ee5cbfSDavid du Colombier }
16480ee5cbfSDavid du Colombier 
1657dd7cddfSDavid du Colombier static void
1667dd7cddfSDavid du Colombier dolisten(char *address)
1677dd7cddfSDavid du Colombier {
16880ee5cbfSDavid du Colombier 	HSPriv *hp;
16980ee5cbfSDavid du Colombier 	HConnect *c;
1709a747e4fSDavid du Colombier 	NetConnInfo *nci;
1717dd7cddfSDavid du Colombier 	char ndir[NETPATHLEN], dir[NETPATHLEN], *p;
17280ee5cbfSDavid du Colombier 	int ctl, nctl, data, t, ok, spotchk;
1739a747e4fSDavid du Colombier 	TLSconn conn;
1747dd7cddfSDavid du Colombier 
17580ee5cbfSDavid du Colombier 	spotchk = 0;
1767dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, "httpd starting");
1777dd7cddfSDavid du Colombier 	ctl = announce(address, dir);
1787dd7cddfSDavid du Colombier 	if(ctl < 0){
1797dd7cddfSDavid du Colombier 		syslog(0, HTTPLOG, "can't announce on %s: %r", address);
1807dd7cddfSDavid du Colombier 		return;
1817dd7cddfSDavid du Colombier 	}
18280ee5cbfSDavid du Colombier 	strcpy(netdirb, dir);
1837dd7cddfSDavid du Colombier 	p = nil;
1847dd7cddfSDavid du Colombier 	if(netdir[0] == '/'){
18580ee5cbfSDavid du Colombier 		p = strchr(netdirb+1, '/');
1867dd7cddfSDavid du Colombier 		if(p != nil)
1877dd7cddfSDavid du Colombier 			*p = '\0';
1887dd7cddfSDavid du Colombier 	}
1897dd7cddfSDavid du Colombier 	if(p == nil)
19080ee5cbfSDavid du Colombier 		strcpy(netdirb, "/net");
19180ee5cbfSDavid du Colombier 	netdir = netdirb;
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 	for(;;){
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 		/*
1967dd7cddfSDavid du Colombier 		 *  wait for a call (or an error)
1977dd7cddfSDavid du Colombier 		 */
1987dd7cddfSDavid du Colombier 		nctl = listen(dir, ndir);
1997dd7cddfSDavid du Colombier 		if(nctl < 0){
2007dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "can't listen on %s: %r", address);
2017dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "ctls = %d", ctl);
2027dd7cddfSDavid du Colombier 			return;
2037dd7cddfSDavid du Colombier 		}
2047dd7cddfSDavid du Colombier 
2057dd7cddfSDavid du Colombier 		/*
2067dd7cddfSDavid du Colombier 		 *  start a process for the service
2077dd7cddfSDavid du Colombier 		 */
2087dd7cddfSDavid du Colombier 		switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
2097dd7cddfSDavid du Colombier 		case -1:
2107dd7cddfSDavid du Colombier 			close(nctl);
2117dd7cddfSDavid du Colombier 			continue;
2127dd7cddfSDavid du Colombier 		case 0:
2137dd7cddfSDavid du Colombier 			/*
2147dd7cddfSDavid du Colombier 			 *  see if we know the service requested
2157dd7cddfSDavid du Colombier 			 */
2167dd7cddfSDavid du Colombier 			data = accept(ctl, ndir);
2179a747e4fSDavid du Colombier 			if(data >= 0 && certificate != nil){
2189a747e4fSDavid du Colombier 				memset(&conn, 0, sizeof(conn));
2199a747e4fSDavid du Colombier 				conn.cert = certificate;
2209a747e4fSDavid du Colombier 				conn.certlen = certlen;
2215ede6b93SDavid du Colombier 				if (certchain != nil)
2225ede6b93SDavid du Colombier 					conn.chain = certchain;
2239a747e4fSDavid du Colombier 				data = tlsServer(data, &conn);
2249a747e4fSDavid du Colombier 			}
2257dd7cddfSDavid du Colombier 			if(data < 0){
2267dd7cddfSDavid du Colombier 				syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
2277dd7cddfSDavid du Colombier 				exits(nil);
2287dd7cddfSDavid du Colombier 			}
2297dd7cddfSDavid du Colombier 			dup(data, 0);
2307dd7cddfSDavid du Colombier 			dup(data, 1);
2317dd7cddfSDavid du Colombier 			dup(data, 2);
2327dd7cddfSDavid du Colombier 			close(data);
2337dd7cddfSDavid du Colombier 			close(ctl);
2347dd7cddfSDavid du Colombier 			close(nctl);
2357dd7cddfSDavid du Colombier 
2369a747e4fSDavid du Colombier 			nci = getnetconninfo(ndir, -1);
2377dd7cddfSDavid du Colombier 			c = mkconnect();
23880ee5cbfSDavid du Colombier 			hp = mkhspriv();
2399a747e4fSDavid du Colombier 			hp->remotesys = nci->rsys;
2409a747e4fSDavid du Colombier 			hp->remoteserv = nci->rserv;
24180ee5cbfSDavid du Colombier 			c->private = hp;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 			hinit(&c->hin, 0, Hread);
2447dd7cddfSDavid du Colombier 			hinit(&c->hout, 1, Hwrite);
2457dd7cddfSDavid du Colombier 
24680ee5cbfSDavid du Colombier 			/*
24780ee5cbfSDavid du Colombier 			 * serve requests until a magic request.
24880ee5cbfSDavid du Colombier 			 * later requests have to come quickly.
24980ee5cbfSDavid du Colombier 			 * only works for http/1.1 or later.
25080ee5cbfSDavid du Colombier 			 */
25180ee5cbfSDavid du Colombier 			for(t = 15*60*1000; ; t = 15*1000){
25280ee5cbfSDavid du Colombier 				if(hparsereq(c, t) <= 0)
25380ee5cbfSDavid du Colombier 					exits(nil);
25480ee5cbfSDavid du Colombier 				ok = doreq(c);
25580ee5cbfSDavid du Colombier 
2569a747e4fSDavid du Colombier 				hflush(&c->hout);
2579a747e4fSDavid du Colombier 
25880ee5cbfSDavid du Colombier 				if(c->head.closeit || ok < 0)
25980ee5cbfSDavid du Colombier 					exits(nil);
26080ee5cbfSDavid du Colombier 
26180ee5cbfSDavid du Colombier 				hreqcleanup(c);
26280ee5cbfSDavid du Colombier 			}
263b85a8364SDavid du Colombier 			/* not reached */
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 		default:
2667dd7cddfSDavid du Colombier 			close(nctl);
2677dd7cddfSDavid du Colombier 			break;
2687dd7cddfSDavid du Colombier 		}
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier 		if(++spotchk > 50){
2717dd7cddfSDavid du Colombier 			spotchk = 0;
2727dd7cddfSDavid du Colombier 			redirectinit();
2737dd7cddfSDavid du Colombier 			contentinit();
2747dd7cddfSDavid du Colombier 			urlinit();
2757dd7cddfSDavid du Colombier 			statsinit();
2767dd7cddfSDavid du Colombier 		}
2777dd7cddfSDavid du Colombier 	}
2787dd7cddfSDavid du Colombier }
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier static int
28180ee5cbfSDavid du Colombier doreq(HConnect *c)
2827dd7cddfSDavid du Colombier {
28380ee5cbfSDavid du Colombier 	HSPriv *hp;
2847dd7cddfSDavid du Colombier 	Strings ss;
285499069deSDavid du Colombier 	char *magic, *uri, *newuri, *origuri, *newpath, *hb;
28680ee5cbfSDavid du Colombier 	char virtualhost[100], logfd0[10], logfd1[10], vers[16];
287*b51d2625SDavid du Colombier 	int n, nredirect;
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier 	/*
29080ee5cbfSDavid du Colombier 	 * munge uri for magic
2917dd7cddfSDavid du Colombier 	 */
29280ee5cbfSDavid du Colombier 	uri = c->req.uri;
293499069deSDavid du Colombier 	nredirect = 0;
294499069deSDavid du Colombier top:
295499069deSDavid du Colombier 	if(++nredirect > 10){
296499069deSDavid du Colombier 		if(hparseheaders(c, 15*60*1000) < 0)
297499069deSDavid du Colombier 			exits("failed");
298dc6ece7cSDavid du Colombier 		werrstr("redirection loop");
299499069deSDavid du Colombier 		return hfail(c, HNotFound, uri);
300499069deSDavid du Colombier 	}
30180ee5cbfSDavid du Colombier 	ss = stripmagic(c, uri);
3027dd7cddfSDavid du Colombier 	uri = ss.s1;
303499069deSDavid du Colombier 	origuri = uri;
3047dd7cddfSDavid du Colombier 	magic = ss.s2;
305499069deSDavid du Colombier 	if(magic)
306499069deSDavid du Colombier 		goto magic;
307499069deSDavid du Colombier 
308499069deSDavid du Colombier 	/*
309499069deSDavid du Colombier 	 * Apply redirects.  Do this before reading headers
310499069deSDavid du Colombier 	 * (if possible) so that we can redirect to magic invisibly.
311499069deSDavid du Colombier 	 */
312499069deSDavid du Colombier 	if(origuri[0]=='/' && origuri[1]=='~'){
313499069deSDavid du Colombier 		n = strlen(origuri) + 4 + UTFmax;
314499069deSDavid du Colombier 		newpath = halloc(c, n);
315499069deSDavid du Colombier 		snprint(newpath, n, "/who/%s", origuri+2);
316499069deSDavid du Colombier 		c->req.uri = newpath;
317499069deSDavid du Colombier 		newuri = newpath;
318499069deSDavid du Colombier 	}else if(origuri[0]=='/' && origuri[1]==0){
319dc6ece7cSDavid du Colombier 		/* can't redirect / until we read the headers below */
320499069deSDavid du Colombier 		newuri = nil;
321499069deSDavid du Colombier 	}else
322499069deSDavid du Colombier 		newuri = redirect(c, origuri);
323499069deSDavid du Colombier 
324499069deSDavid du Colombier 	if(newuri != nil){
325dc6ece7cSDavid du Colombier 		if(isdecorated(newuri)){
326*b51d2625SDavid du Colombier 			if(newuri[0] == Modperm) {
327*b51d2625SDavid du Colombier 				logit(c, "%s: permanently moved to %s",
328*b51d2625SDavid du Colombier 					origuri, undecorated(newuri));
329*b51d2625SDavid du Colombier 				return hmoved(c, undecorated(newuri));
330*b51d2625SDavid du Colombier 			}
331dc6ece7cSDavid du Colombier 			c->req.uri = uri = undecorated(newuri);
332*b51d2625SDavid du Colombier //			logit(c, "%s: silent replacement %s", origuri, uri);
333499069deSDavid du Colombier 			goto top;
334499069deSDavid du Colombier 		}
335499069deSDavid du Colombier 		if(hparseheaders(c, 15*60*1000) < 0)
336499069deSDavid du Colombier 			exits("failed");
337dc6ece7cSDavid du Colombier 		/*
338dc6ece7cSDavid du Colombier 		 * try temporary redirect instead of permanent,
339dc6ece7cSDavid du Colombier 		 */
340dc6ece7cSDavid du Colombier 		if (http11(c))
341dc6ece7cSDavid du Colombier 			return hredirected(c, "307 Temporary Redirect", newuri);
342dc6ece7cSDavid du Colombier 		else
343dc6ece7cSDavid du Colombier 			return hredirected(c, "302 Temporary Redirect", newuri);
344499069deSDavid du Colombier 	}
3457dd7cddfSDavid du Colombier 
34680ee5cbfSDavid du Colombier 	/*
34780ee5cbfSDavid du Colombier 	 * for magic we exec a new program and serve no more requests
34880ee5cbfSDavid du Colombier 	 */
349499069deSDavid du Colombier magic:
35080ee5cbfSDavid du Colombier 	if(magic != nil && strcmp(magic, "httpd") != 0){
35180ee5cbfSDavid du Colombier 		snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
35280ee5cbfSDavid du Colombier 		snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
35380ee5cbfSDavid du Colombier 		snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
35480ee5cbfSDavid du Colombier 		snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
35580ee5cbfSDavid du Colombier 		hb = hunload(&c->hin);
3569a747e4fSDavid du Colombier 		if(hb == nil){
35780ee5cbfSDavid du Colombier 			hfail(c, HInternal);
3589a747e4fSDavid du Colombier 			return -1;
3599a747e4fSDavid du Colombier 		}
36080ee5cbfSDavid du Colombier 		hp = c->private;
36180ee5cbfSDavid du Colombier 		execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot, "-r", hp->remotesys, "-N", netdir, "-b", hb,
36280ee5cbfSDavid du Colombier 			"-L", logfd0, logfd1, "-R", c->header,
36380ee5cbfSDavid du Colombier 			c->req.meth, vers, uri, c->req.search, nil);
36480ee5cbfSDavid du Colombier 		logit(c, "no magic %s uri %s", magic, uri);
36580ee5cbfSDavid du Colombier 		hfail(c, HNotFound, uri);
36680ee5cbfSDavid du Colombier 		return -1;
36780ee5cbfSDavid du Colombier 	}
3687dd7cddfSDavid du Colombier 
3697dd7cddfSDavid du Colombier 	/*
3707dd7cddfSDavid du Colombier 	 * normal case is just file transfer
3717dd7cddfSDavid du Colombier 	 */
37280ee5cbfSDavid du Colombier 	if(hparseheaders(c, 15*60*1000) < 0)
37380ee5cbfSDavid du Colombier 		exits("failed");
374499069deSDavid du Colombier 	if(origuri[0] == '/' && origuri[1] == 0){
375499069deSDavid du Colombier 		snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
376499069deSDavid du Colombier 		newuri = redirect(c, virtualhost);
377499069deSDavid du Colombier 		if(newuri == nil)
378499069deSDavid du Colombier 			newuri = redirect(c, origuri);
379499069deSDavid du Colombier 		if(newuri)
380499069deSDavid du Colombier 			return hmoved(c, newuri);
381499069deSDavid du Colombier 	}
3827dd7cddfSDavid du Colombier 	if(!http11(c) && !c->head.persist)
3837dd7cddfSDavid du Colombier 		c->head.closeit = 1;
38480ee5cbfSDavid du Colombier 	return send(c);
3857dd7cddfSDavid du Colombier }
3867dd7cddfSDavid du Colombier 
3877dd7cddfSDavid du Colombier static int
38880ee5cbfSDavid du Colombier send(HConnect *c)
3897dd7cddfSDavid du Colombier {
3909a747e4fSDavid du Colombier 	Dir *dir;
391fe853e23SDavid du Colombier 	char *w, *w2, *p, *masque;
3927dd7cddfSDavid du Colombier 	int fd, fd1, n, force301, ok;
3937dd7cddfSDavid du Colombier 
3947dd7cddfSDavid du Colombier 	if(c->req.search)
39580ee5cbfSDavid du Colombier 		return hfail(c, HNoSearch, c->req.uri);
3967dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
39780ee5cbfSDavid du Colombier 		return hunallowed(c, "GET, HEAD");
3987dd7cddfSDavid du Colombier 	if(c->head.expectother || c->head.expectcont)
39980ee5cbfSDavid du Colombier 		return hfail(c, HExpectFail);
4007dd7cddfSDavid du Colombier 
4019a747e4fSDavid du Colombier 	masque = masquerade(c->head.host);
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier 	/*
4047dd7cddfSDavid du Colombier 	 * check for directory/file mismatch with trailing /,
4057dd7cddfSDavid du Colombier 	 * and send any redirections.
4067dd7cddfSDavid du Colombier 	 */
4079a747e4fSDavid du Colombier 	n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
4089a747e4fSDavid du Colombier 		STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
40980ee5cbfSDavid du Colombier 	w = halloc(c, n);
4107dd7cddfSDavid du Colombier 	strcpy(w, webroot);
4119a747e4fSDavid du Colombier 	strcat(w, masque);
4129a747e4fSDavid du Colombier 	strcat(w, c->req.uri);
4133ff48bf5SDavid du Colombier 
4143ff48bf5SDavid du Colombier 	/*
415fe853e23SDavid du Colombier 	 *  favicon can be overridden by hostname.ico
416fe853e23SDavid du Colombier 	 */
417fe853e23SDavid du Colombier 	if(strcmp(c->req.uri, "/favicon.ico") == 0){
418fe853e23SDavid du Colombier 		w2 = halloc(c, n+strlen(c->head.host)+2);
419fe853e23SDavid du Colombier 		strcpy(w2, webroot);
420fe853e23SDavid du Colombier 		strcat(w2, masque);
421fe853e23SDavid du Colombier 		strcat(w2, "/");
422fe853e23SDavid du Colombier 		strcat(w2, c->head.host);
423fe853e23SDavid du Colombier 		strcat(w2, ".ico");
424fe853e23SDavid du Colombier 		if(access(w2, AREAD)==0)
425fe853e23SDavid du Colombier 			w = w2;
426fe853e23SDavid du Colombier 	}
427fe853e23SDavid du Colombier 
428fe853e23SDavid du Colombier 	/*
4293ff48bf5SDavid du Colombier 	 * don't show the contents of .httplogin
4303ff48bf5SDavid du Colombier 	 */
4313ff48bf5SDavid du Colombier 	n = strlen(w);
4323ff48bf5SDavid du Colombier 	if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
4333ff48bf5SDavid du Colombier 		return notfound(c, c->req.uri);
4343ff48bf5SDavid du Colombier 
4359a747e4fSDavid du Colombier 	fd = open(w, OREAD);
4369a747e4fSDavid du Colombier 	if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
4379a747e4fSDavid du Colombier 		// may be a URI from before virtual hosts;  try again without masque
4389a747e4fSDavid du Colombier 		strcpy(w, webroot);
4397dd7cddfSDavid du Colombier 		strcat(w, c->req.uri);
4407dd7cddfSDavid du Colombier 		fd = open(w, OREAD);
4419a747e4fSDavid du Colombier 	}
4427dd7cddfSDavid du Colombier 	if(fd < 0)
4437dd7cddfSDavid du Colombier 		return notfound(c, c->req.uri);
4449a747e4fSDavid du Colombier 	dir = dirfstat(fd);
4459a747e4fSDavid du Colombier 	if(dir == nil){
4467dd7cddfSDavid du Colombier 		close(fd);
44780ee5cbfSDavid du Colombier 		return hfail(c, HInternal);
4487dd7cddfSDavid du Colombier 	}
4497dd7cddfSDavid du Colombier 	p = strchr(w, '\0');
4509a747e4fSDavid du Colombier 	if(dir->mode & DMDIR){
4519a747e4fSDavid du Colombier 		free(dir);
4527dd7cddfSDavid du Colombier 		if(p > w && p[-1] == '/'){
4537dd7cddfSDavid du Colombier 			strcat(w, "index.html");
4547dd7cddfSDavid du Colombier 			force301 = 0;
4557dd7cddfSDavid du Colombier 		}else{
4567dd7cddfSDavid du Colombier 			strcat(w, "/index.html");
4577dd7cddfSDavid du Colombier 			force301 = 1;
4587dd7cddfSDavid du Colombier 		}
4597dd7cddfSDavid du Colombier 		fd1 = open(w, OREAD);
4607dd7cddfSDavid du Colombier 		if(fd1 < 0){
4617dd7cddfSDavid du Colombier 			close(fd);
4627dd7cddfSDavid du Colombier 			return notfound(c, c->req.uri);
4637dd7cddfSDavid du Colombier 		}
4649a747e4fSDavid du Colombier 		c->req.uri = w + strlen(webroot) + strlen(masque);
4657dd7cddfSDavid du Colombier 		if(force301 && c->req.vermaj){
4667dd7cddfSDavid du Colombier 			close(fd);
4677dd7cddfSDavid du Colombier 			close(fd1);
46880ee5cbfSDavid du Colombier 			return hmoved(c, c->req.uri);
4697dd7cddfSDavid du Colombier 		}
4707dd7cddfSDavid du Colombier 		close(fd);
4717dd7cddfSDavid du Colombier 		fd = fd1;
4729a747e4fSDavid du Colombier 		dir = dirfstat(fd);
4739a747e4fSDavid du Colombier 		if(dir == nil){
4747dd7cddfSDavid du Colombier 			close(fd);
47580ee5cbfSDavid du Colombier 			return hfail(c, HInternal);
4767dd7cddfSDavid du Colombier 		}
4777dd7cddfSDavid du Colombier 	}else if(p > w && p[-1] == '/'){
4789a747e4fSDavid du Colombier 		free(dir);
4797dd7cddfSDavid du Colombier 		close(fd);
4807dd7cddfSDavid du Colombier 		*strrchr(c->req.uri, '/') = '\0';
48180ee5cbfSDavid du Colombier 		return hmoved(c, c->req.uri);
4827dd7cddfSDavid du Colombier 	}
4837dd7cddfSDavid du Colombier 
4849a747e4fSDavid du Colombier 	ok = authorize(c, w);
4859a747e4fSDavid du Colombier 	if(ok <= 0){
4869a747e4fSDavid du Colombier 		free(dir);
4879a747e4fSDavid du Colombier 		close(fd);
4889a747e4fSDavid du Colombier 		return ok;
4899a747e4fSDavid du Colombier 	}
4909a747e4fSDavid du Colombier 
4919a747e4fSDavid du Colombier 	return sendfd(c, fd, dir, nil, nil);
4927dd7cddfSDavid du Colombier }
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier static Strings
49580ee5cbfSDavid du Colombier stripmagic(HConnect *hc, char *uri)
4967dd7cddfSDavid du Colombier {
4977dd7cddfSDavid du Colombier 	Strings ss;
4987dd7cddfSDavid du Colombier 	char *newuri, *prog, *s;
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier 	prog = stripprefix("/magic/", uri);
5017dd7cddfSDavid du Colombier 	if(prog == nil){
5027dd7cddfSDavid du Colombier 		ss.s1 = uri;
5037dd7cddfSDavid du Colombier 		ss.s2 = nil;
5047dd7cddfSDavid du Colombier 		return ss;
5057dd7cddfSDavid du Colombier 	}
5067dd7cddfSDavid du Colombier 
5077dd7cddfSDavid du Colombier 	s = strchr(prog, '/');
5087dd7cddfSDavid du Colombier 	if(s == nil)
5097dd7cddfSDavid du Colombier 		newuri = "";
5107dd7cddfSDavid du Colombier 	else{
51180ee5cbfSDavid du Colombier 		newuri = hstrdup(hc, s);
5127dd7cddfSDavid du Colombier 		*s = 0;
5137dd7cddfSDavid du Colombier 		s = strrchr(s, '/');
5147dd7cddfSDavid du Colombier 		if(s != nil && s[1] == 0)
5157dd7cddfSDavid du Colombier 			*s = 0;
5167dd7cddfSDavid du Colombier 	}
5177dd7cddfSDavid du Colombier 	ss.s1 = newuri;
5187dd7cddfSDavid du Colombier 	ss.s2 = prog;
5197dd7cddfSDavid du Colombier 	return ss;
5207dd7cddfSDavid du Colombier }
5217dd7cddfSDavid du Colombier 
5227dd7cddfSDavid du Colombier static char*
5237dd7cddfSDavid du Colombier stripprefix(char *pre, char *str)
5247dd7cddfSDavid du Colombier {
5257dd7cddfSDavid du Colombier 	while(*pre)
5267dd7cddfSDavid du Colombier 		if(*str++ != *pre++)
5277dd7cddfSDavid du Colombier 			return nil;
5287dd7cddfSDavid du Colombier 	return str;
5297dd7cddfSDavid du Colombier }
5307dd7cddfSDavid du Colombier 
5319a747e4fSDavid du Colombier /*
5329a747e4fSDavid du Colombier  * couldn't open a file
5339a747e4fSDavid du Colombier  * figure out why and return and error message
5349a747e4fSDavid du Colombier  */
5359a747e4fSDavid du Colombier static int
5369a747e4fSDavid du Colombier notfound(HConnect *c, char *url)
5379a747e4fSDavid du Colombier {
5389a747e4fSDavid du Colombier 	c->xferbuf[0] = 0;
539dc6ece7cSDavid du Colombier 	rerrstr(c->xferbuf, sizeof c->xferbuf);
5409a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "file does not exist") != nil)
5419a747e4fSDavid du Colombier 		return hfail(c, HNotFound, url);
5429a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "permission denied") != nil)
5439a747e4fSDavid du Colombier 		return hfail(c, HUnauth, url);
5449a747e4fSDavid du Colombier 	return hfail(c, HNotFound, url);
5459a747e4fSDavid du Colombier }
5469a747e4fSDavid du Colombier 
5477dd7cddfSDavid du Colombier static char*
5487dd7cddfSDavid du Colombier sysdom(void)
5497dd7cddfSDavid du Colombier {
5507dd7cddfSDavid du Colombier 	char *dn;
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier 	dn = csquery("sys" , sysname(), "dom");
5537dd7cddfSDavid du Colombier 	if(dn == nil)
5547dd7cddfSDavid du Colombier 		dn = "who cares";
5557dd7cddfSDavid du Colombier 	return dn;
5567dd7cddfSDavid du Colombier }
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier /*
5597dd7cddfSDavid du Colombier  *  query the connection server
5607dd7cddfSDavid du Colombier  */
5617dd7cddfSDavid du Colombier static char*
5627dd7cddfSDavid du Colombier csquery(char *attr, char *val, char *rattr)
5637dd7cddfSDavid du Colombier {
5647dd7cddfSDavid du Colombier 	char token[64+4];
5657dd7cddfSDavid du Colombier 	char buf[256], *p, *sp;
5667dd7cddfSDavid du Colombier 	int fd, n;
5677dd7cddfSDavid du Colombier 
5687dd7cddfSDavid du Colombier 	if(val == nil || val[0] == 0)
5697dd7cddfSDavid du Colombier 		return nil;
5707dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", netdir);
5717dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
5727dd7cddfSDavid du Colombier 	if(fd < 0)
5737dd7cddfSDavid du Colombier 		return nil;
5747dd7cddfSDavid du Colombier 	fprint(fd, "!%s=%s", attr, val);
5757dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
5767dd7cddfSDavid du Colombier 	snprint(token, sizeof(token), "%s=", rattr);
5777dd7cddfSDavid du Colombier 	for(;;){
5787dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf)-1);
5797dd7cddfSDavid du Colombier 		if(n <= 0)
5807dd7cddfSDavid du Colombier 			break;
5817dd7cddfSDavid du Colombier 		buf[n] = 0;
5827dd7cddfSDavid du Colombier 		p = strstr(buf, token);
5837dd7cddfSDavid du Colombier 		if(p != nil && (p == buf || *(p-1) == 0)){
5847dd7cddfSDavid du Colombier 			close(fd);
5857dd7cddfSDavid du Colombier 			sp = strchr(p, ' ');
5867dd7cddfSDavid du Colombier 			if(sp)
5877dd7cddfSDavid du Colombier 				*sp = 0;
5887dd7cddfSDavid du Colombier 			p = strchr(p, '=');
5897dd7cddfSDavid du Colombier 			if(p == nil)
5907dd7cddfSDavid du Colombier 				return nil;
5917dd7cddfSDavid du Colombier 			return estrdup(p+1);
5927dd7cddfSDavid du Colombier 		}
5937dd7cddfSDavid du Colombier 	}
5947dd7cddfSDavid du Colombier 	close(fd);
5957dd7cddfSDavid du Colombier 	return nil;
5967dd7cddfSDavid du Colombier }
597