xref: /plan9/sys/src/cmd/ip/httpd/httpd.c (revision fe853e2326f51910bb38886e9bfc22ecdef993d7)
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;
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier void
387dd7cddfSDavid du Colombier usage(void)
397dd7cddfSDavid du Colombier {
407dd7cddfSDavid du Colombier 	fprint(2, "usage: httpd [-a srvaddress] [-d domain] [-n namespace] [-w webroot]\n");
417dd7cddfSDavid du Colombier 	exits("usage");
427dd7cddfSDavid du Colombier }
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier void
457dd7cddfSDavid du Colombier main(int argc, char **argv)
467dd7cddfSDavid du Colombier {
477dd7cddfSDavid du Colombier 	char *address;
487dd7cddfSDavid du Colombier 
497dd7cddfSDavid du Colombier 	namespace = nil;
507dd7cddfSDavid du Colombier 	address = nil;
5180ee5cbfSDavid du Colombier 	hmydomain = nil;
5280ee5cbfSDavid du Colombier 	netdir = "/net";
539a747e4fSDavid du Colombier 	fmtinstall('D', hdatefmt);
549a747e4fSDavid du Colombier 	fmtinstall('H', httpfmt);
559a747e4fSDavid du Colombier 	fmtinstall('U', hurlfmt);
567dd7cddfSDavid du Colombier 	ARGBEGIN{
579a747e4fSDavid du Colombier 	case 'c':
589a747e4fSDavid du Colombier 		certificate = readcert(ARGF(), &certlen);
599a747e4fSDavid du Colombier 		if(certificate == nil)
60d9306527SDavid du Colombier 			sysfatal("reading certificate: %r");
619a747e4fSDavid du Colombier 		break;
627dd7cddfSDavid du Colombier 	case 'n':
637dd7cddfSDavid du Colombier 		namespace = ARGF();
647dd7cddfSDavid du Colombier 		break;
657dd7cddfSDavid du Colombier 	case 'a':
667dd7cddfSDavid du Colombier 		address = ARGF();
677dd7cddfSDavid du Colombier 		break;
687dd7cddfSDavid du Colombier 	case 'd':
6980ee5cbfSDavid du Colombier 		hmydomain = ARGF();
707dd7cddfSDavid du Colombier 		break;
717dd7cddfSDavid du Colombier 	case 'w':
727dd7cddfSDavid du Colombier 		webroot = ARGF();
737dd7cddfSDavid du Colombier 		break;
747dd7cddfSDavid du Colombier 	default:
757dd7cddfSDavid du Colombier 		usage();
767dd7cddfSDavid du Colombier 		break;
777dd7cddfSDavid du Colombier 	}ARGEND
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 	if(argc)
807dd7cddfSDavid du Colombier 		usage();
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier 	if(namespace == nil)
837dd7cddfSDavid du Colombier 		namespace = "/lib/namespace.httpd";
847dd7cddfSDavid du Colombier 	if(address == nil)
859a747e4fSDavid du Colombier 		address = "*";
867dd7cddfSDavid du Colombier 	if(webroot == nil)
877dd7cddfSDavid du Colombier 		webroot = "/usr/web";
887dd7cddfSDavid du Colombier 	else{
897dd7cddfSDavid du Colombier 		cleanname(webroot);
907dd7cddfSDavid du Colombier 		if(webroot[0] != '/')
917dd7cddfSDavid du Colombier 			webroot = "/usr/web";
927dd7cddfSDavid du Colombier 	}
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier 	switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
957dd7cddfSDavid du Colombier 	case -1:
967dd7cddfSDavid du Colombier 		sysfatal("fork");
977dd7cddfSDavid du Colombier 	case 0:
987dd7cddfSDavid du Colombier 		break;
997dd7cddfSDavid du Colombier 	default:
1007dd7cddfSDavid du Colombier 		exits(nil);
1017dd7cddfSDavid du Colombier 	}
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier 	/*
1047dd7cddfSDavid du Colombier 	 * open all files we might need before castrating namespace
1057dd7cddfSDavid du Colombier 	 */
1067dd7cddfSDavid du Colombier 	time(nil);
10780ee5cbfSDavid du Colombier 	if(hmydomain == nil)
10880ee5cbfSDavid du Colombier 		hmydomain = sysdom();
1097dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, nil);
1107dd7cddfSDavid du Colombier 	logall[0] = open("/sys/log/httpd/0", OWRITE);
1117dd7cddfSDavid du Colombier 	logall[1] = open("/sys/log/httpd/1", OWRITE);
1127dd7cddfSDavid du Colombier 	redirectinit();
1137dd7cddfSDavid du Colombier 	contentinit();
1147dd7cddfSDavid du Colombier 	urlinit();
1157dd7cddfSDavid du Colombier 	statsinit();
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	becomenone(namespace);
1189a747e4fSDavid du Colombier 	dolisten(netmkaddr(address, "tcp", certificate == nil ? "http" : "https"));
1197dd7cddfSDavid du Colombier 	exits(nil);
1207dd7cddfSDavid du Colombier }
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier static void
1237dd7cddfSDavid du Colombier becomenone(char *namespace)
1247dd7cddfSDavid du Colombier {
1257dd7cddfSDavid du Colombier 	int fd;
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier 	fd = open("#c/user", OWRITE);
1287dd7cddfSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
1297dd7cddfSDavid du Colombier 		sysfatal("can't become none");
1307dd7cddfSDavid du Colombier 	close(fd);
1317dd7cddfSDavid du Colombier 	if(newns("none", nil) < 0)
1327dd7cddfSDavid du Colombier 		sysfatal("can't build normal namespace");
1337dd7cddfSDavid du Colombier 	if(addns("none", namespace) < 0)
1347dd7cddfSDavid du Colombier 		sysfatal("can't build httpd namespace");
1357dd7cddfSDavid du Colombier }
1367dd7cddfSDavid du Colombier 
13780ee5cbfSDavid du Colombier static HConnect*
1387dd7cddfSDavid du Colombier mkconnect(void)
1397dd7cddfSDavid du Colombier {
14080ee5cbfSDavid du Colombier 	HConnect *c;
1417dd7cddfSDavid du Colombier 
14280ee5cbfSDavid du Colombier 	c = ezalloc(sizeof(HConnect));
1437dd7cddfSDavid du Colombier 	c->hpos = c->header;
1447dd7cddfSDavid du Colombier 	c->hstop = c->header;
14580ee5cbfSDavid du Colombier 	c->replog = writelog;
1467dd7cddfSDavid du Colombier 	return c;
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier 
14980ee5cbfSDavid du Colombier static HSPriv*
15080ee5cbfSDavid du Colombier mkhspriv(void)
15180ee5cbfSDavid du Colombier {
15280ee5cbfSDavid du Colombier 	HSPriv *p;
15380ee5cbfSDavid du Colombier 
15480ee5cbfSDavid du Colombier 	p = ezalloc(sizeof(HSPriv));
15580ee5cbfSDavid du Colombier 	return p;
15680ee5cbfSDavid du Colombier }
15780ee5cbfSDavid du Colombier 
1587dd7cddfSDavid du Colombier static void
1597dd7cddfSDavid du Colombier dolisten(char *address)
1607dd7cddfSDavid du Colombier {
16180ee5cbfSDavid du Colombier 	HSPriv *hp;
16280ee5cbfSDavid du Colombier 	HConnect *c;
1639a747e4fSDavid du Colombier 	NetConnInfo *nci;
1647dd7cddfSDavid du Colombier 	char ndir[NETPATHLEN], dir[NETPATHLEN], *p;
16580ee5cbfSDavid du Colombier 	int ctl, nctl, data, t, ok, spotchk;
1669a747e4fSDavid du Colombier 	TLSconn conn;
1677dd7cddfSDavid du Colombier 
16880ee5cbfSDavid du Colombier 	spotchk = 0;
1697dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, "httpd starting");
1707dd7cddfSDavid du Colombier 	ctl = announce(address, dir);
1717dd7cddfSDavid du Colombier 	if(ctl < 0){
1727dd7cddfSDavid du Colombier 		syslog(0, HTTPLOG, "can't announce on %s: %r", address);
1739a747e4fSDavid du Colombier fprint(2, "failed: %d\n", getpid());
1749a747e4fSDavid du Colombier for(;;)sleep(1000);
1757dd7cddfSDavid du Colombier 		return;
1767dd7cddfSDavid du Colombier 	}
17780ee5cbfSDavid du Colombier 	strcpy(netdirb, dir);
1787dd7cddfSDavid du Colombier 	p = nil;
1797dd7cddfSDavid du Colombier 	if(netdir[0] == '/'){
18080ee5cbfSDavid du Colombier 		p = strchr(netdirb+1, '/');
1817dd7cddfSDavid du Colombier 		if(p != nil)
1827dd7cddfSDavid du Colombier 			*p = '\0';
1837dd7cddfSDavid du Colombier 	}
1847dd7cddfSDavid du Colombier 	if(p == nil)
18580ee5cbfSDavid du Colombier 		strcpy(netdirb, "/net");
18680ee5cbfSDavid du Colombier 	netdir = netdirb;
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier 	for(;;){
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 		/*
1917dd7cddfSDavid du Colombier 		 *  wait for a call (or an error)
1927dd7cddfSDavid du Colombier 		 */
1937dd7cddfSDavid du Colombier 		nctl = listen(dir, ndir);
1947dd7cddfSDavid du Colombier 		if(nctl < 0){
1957dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "can't listen on %s: %r", address);
1967dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "ctls = %d", ctl);
1977dd7cddfSDavid du Colombier 			return;
1987dd7cddfSDavid du Colombier 		}
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 		/*
2017dd7cddfSDavid du Colombier 		 *  start a process for the service
2027dd7cddfSDavid du Colombier 		 */
2037dd7cddfSDavid du Colombier 		switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
2047dd7cddfSDavid du Colombier 		case -1:
2057dd7cddfSDavid du Colombier 			close(nctl);
2067dd7cddfSDavid du Colombier 			continue;
2077dd7cddfSDavid du Colombier 		case 0:
2087dd7cddfSDavid du Colombier 			/*
2097dd7cddfSDavid du Colombier 			 *  see if we know the service requested
2107dd7cddfSDavid du Colombier 			 */
2117dd7cddfSDavid du Colombier 			data = accept(ctl, ndir);
2129a747e4fSDavid du Colombier 			if(data >= 0 && certificate != nil){
2139a747e4fSDavid du Colombier 				memset(&conn, 0, sizeof(conn));
2149a747e4fSDavid du Colombier 				conn.cert = certificate;
2159a747e4fSDavid du Colombier 				conn.certlen = certlen;
2169a747e4fSDavid du Colombier 				data = tlsServer(data, &conn);
2179a747e4fSDavid du Colombier 			}
2187dd7cddfSDavid du Colombier 			if(data < 0){
2197dd7cddfSDavid du Colombier 				syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
2207dd7cddfSDavid du Colombier 				exits(nil);
2217dd7cddfSDavid du Colombier 			}
2227dd7cddfSDavid du Colombier 			dup(data, 0);
2237dd7cddfSDavid du Colombier 			dup(data, 1);
2247dd7cddfSDavid du Colombier 			dup(data, 2);
2257dd7cddfSDavid du Colombier 			close(data);
2267dd7cddfSDavid du Colombier 			close(ctl);
2277dd7cddfSDavid du Colombier 			close(nctl);
2287dd7cddfSDavid du Colombier 
2299a747e4fSDavid du Colombier 			nci = getnetconninfo(ndir, -1);
2307dd7cddfSDavid du Colombier 			c = mkconnect();
23180ee5cbfSDavid du Colombier 			hp = mkhspriv();
2329a747e4fSDavid du Colombier 			hp->remotesys = nci->rsys;
2339a747e4fSDavid du Colombier 			hp->remoteserv = nci->rserv;
23480ee5cbfSDavid du Colombier 			c->private = hp;
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 			hinit(&c->hin, 0, Hread);
2377dd7cddfSDavid du Colombier 			hinit(&c->hout, 1, Hwrite);
2387dd7cddfSDavid du Colombier 
23980ee5cbfSDavid du Colombier 			/*
24080ee5cbfSDavid du Colombier 			 * serve requests until a magic request.
24180ee5cbfSDavid du Colombier 			 * later requests have to come quickly.
24280ee5cbfSDavid du Colombier 			 * only works for http/1.1 or later.
24380ee5cbfSDavid du Colombier 			 */
24480ee5cbfSDavid du Colombier 			for(t = 15*60*1000; ; t = 15*1000){
24580ee5cbfSDavid du Colombier 				if(hparsereq(c, t) <= 0)
24680ee5cbfSDavid du Colombier 					exits(nil);
24780ee5cbfSDavid du Colombier 				ok = doreq(c);
24880ee5cbfSDavid du Colombier 
2499a747e4fSDavid du Colombier 				hflush(&c->hout);
2509a747e4fSDavid du Colombier 
25180ee5cbfSDavid du Colombier 				if(c->head.closeit || ok < 0)
25280ee5cbfSDavid du Colombier 					exits(nil);
25380ee5cbfSDavid du Colombier 
25480ee5cbfSDavid du Colombier 				hreqcleanup(c);
25580ee5cbfSDavid du Colombier 			}
2567dd7cddfSDavid du Colombier 
2577dd7cddfSDavid du Colombier 			exits(nil);
2587dd7cddfSDavid du Colombier 		default:
2597dd7cddfSDavid du Colombier 			close(nctl);
2607dd7cddfSDavid du Colombier 			break;
2617dd7cddfSDavid du Colombier 		}
2627dd7cddfSDavid du Colombier 
2637dd7cddfSDavid du Colombier 		if(++spotchk > 50){
2647dd7cddfSDavid du Colombier 			spotchk = 0;
2657dd7cddfSDavid du Colombier 			redirectinit();
2667dd7cddfSDavid du Colombier 			contentinit();
2677dd7cddfSDavid du Colombier 			urlinit();
2687dd7cddfSDavid du Colombier 			statsinit();
2697dd7cddfSDavid du Colombier 		}
2707dd7cddfSDavid du Colombier 	}
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier static int
27480ee5cbfSDavid du Colombier doreq(HConnect *c)
2757dd7cddfSDavid du Colombier {
27680ee5cbfSDavid du Colombier 	HSPriv *hp;
2777dd7cddfSDavid du Colombier 	Strings ss;
27880ee5cbfSDavid du Colombier 	char *magic, *uri, *origuri, *newpath, *hb;
27980ee5cbfSDavid du Colombier 	char virtualhost[100], logfd0[10], logfd1[10], vers[16];
28080ee5cbfSDavid du Colombier 	int n;
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	/*
28380ee5cbfSDavid du Colombier 	 * munge uri for magic
2847dd7cddfSDavid du Colombier 	 */
28580ee5cbfSDavid du Colombier 	uri = c->req.uri;
28680ee5cbfSDavid du Colombier 	ss = stripmagic(c, uri);
2877dd7cddfSDavid du Colombier 	uri = ss.s1;
2887dd7cddfSDavid du Colombier 	magic = ss.s2;
2897dd7cddfSDavid du Colombier 
29080ee5cbfSDavid du Colombier 	/*
29180ee5cbfSDavid du Colombier 	 * for magic we exec a new program and serve no more requests
29280ee5cbfSDavid du Colombier 	 */
29380ee5cbfSDavid du Colombier 	if(magic != nil && strcmp(magic, "httpd") != 0){
29480ee5cbfSDavid du Colombier 		snprint(c->xferbuf, HBufSize, "/bin/ip/httpd/%s", magic);
29580ee5cbfSDavid du Colombier 		snprint(logfd0, sizeof(logfd0), "%d", logall[0]);
29680ee5cbfSDavid du Colombier 		snprint(logfd1, sizeof(logfd1), "%d", logall[1]);
29780ee5cbfSDavid du Colombier 		snprint(vers, sizeof(vers), "HTTP/%d.%d", c->req.vermaj, c->req.vermin);
29880ee5cbfSDavid du Colombier 		hb = hunload(&c->hin);
2999a747e4fSDavid du Colombier 		if(hb == nil){
30080ee5cbfSDavid du Colombier 			hfail(c, HInternal);
3019a747e4fSDavid du Colombier 			return -1;
3029a747e4fSDavid du Colombier 		}
30380ee5cbfSDavid du Colombier 		hp = c->private;
30480ee5cbfSDavid du Colombier 		execl(c->xferbuf, magic, "-d", hmydomain, "-w", webroot, "-r", hp->remotesys, "-N", netdir, "-b", hb,
30580ee5cbfSDavid du Colombier 			"-L", logfd0, logfd1, "-R", c->header,
30680ee5cbfSDavid du Colombier 			c->req.meth, vers, uri, c->req.search, nil);
30780ee5cbfSDavid du Colombier 		logit(c, "no magic %s uri %s", magic, uri);
30880ee5cbfSDavid du Colombier 		hfail(c, HNotFound, uri);
30980ee5cbfSDavid du Colombier 		return -1;
31080ee5cbfSDavid du Colombier 	}
3117dd7cddfSDavid du Colombier 
3127dd7cddfSDavid du Colombier 	/*
3137dd7cddfSDavid du Colombier 	 * normal case is just file transfer
3147dd7cddfSDavid du Colombier 	 */
3157dd7cddfSDavid du Colombier 	origuri = uri;
31680ee5cbfSDavid du Colombier 	if(hparseheaders(c, 15*60*1000) < 0)
31780ee5cbfSDavid du Colombier 		exits("failed");
3187dd7cddfSDavid du Colombier 	if(!http11(c) && !c->head.persist)
3197dd7cddfSDavid du Colombier 		c->head.closeit = 1;
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier 	if(origuri[0]=='/' && origuri[1]=='~'){
3227dd7cddfSDavid du Colombier 		n = strlen(origuri) + 4 + UTFmax;
32380ee5cbfSDavid du Colombier 		newpath = halloc(c, n);
3247dd7cddfSDavid du Colombier 		snprint(newpath, n, "/who/%s", origuri+2);
3257dd7cddfSDavid du Colombier 		c->req.uri = newpath;
3267dd7cddfSDavid du Colombier 		uri = newpath;
3277dd7cddfSDavid du Colombier 	}else if(origuri[0]=='/' && origuri[1]==0){
3287dd7cddfSDavid du Colombier 		snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
32980ee5cbfSDavid du Colombier 		uri = redirect(c, virtualhost);
3307dd7cddfSDavid du Colombier 		if(uri == nil)
33180ee5cbfSDavid du Colombier 			uri = redirect(c, origuri);
3327dd7cddfSDavid du Colombier 	}else
33380ee5cbfSDavid du Colombier 		uri = redirect(c, origuri);
3347dd7cddfSDavid du Colombier 
33580ee5cbfSDavid du Colombier 	if(uri != nil)
33680ee5cbfSDavid du Colombier 		return hmoved(c, uri);
33780ee5cbfSDavid du Colombier 	return send(c);
3387dd7cddfSDavid du Colombier }
3397dd7cddfSDavid du Colombier 
3407dd7cddfSDavid du Colombier static int
34180ee5cbfSDavid du Colombier send(HConnect *c)
3427dd7cddfSDavid du Colombier {
3439a747e4fSDavid du Colombier 	Dir *dir;
344*fe853e23SDavid du Colombier 	char *w, *w2, *p, *masque;
3457dd7cddfSDavid du Colombier 	int fd, fd1, n, force301, ok;
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 	if(c->req.search)
34880ee5cbfSDavid du Colombier 		return hfail(c, HNoSearch, c->req.uri);
3497dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
35080ee5cbfSDavid du Colombier 		return hunallowed(c, "GET, HEAD");
3517dd7cddfSDavid du Colombier 	if(c->head.expectother || c->head.expectcont)
35280ee5cbfSDavid du Colombier 		return hfail(c, HExpectFail);
3537dd7cddfSDavid du Colombier 
3549a747e4fSDavid du Colombier 	masque = masquerade(c->head.host);
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 	/*
3577dd7cddfSDavid du Colombier 	 * check for directory/file mismatch with trailing /,
3587dd7cddfSDavid du Colombier 	 * and send any redirections.
3597dd7cddfSDavid du Colombier 	 */
3609a747e4fSDavid du Colombier 	n = strlen(webroot) + strlen(masque) + strlen(c->req.uri) +
3619a747e4fSDavid du Colombier 		STRLEN("/index.html") + STRLEN("/.httplogin") + 1;
36280ee5cbfSDavid du Colombier 	w = halloc(c, n);
3637dd7cddfSDavid du Colombier 	strcpy(w, webroot);
3649a747e4fSDavid du Colombier 	strcat(w, masque);
3659a747e4fSDavid du Colombier 	strcat(w, c->req.uri);
3663ff48bf5SDavid du Colombier 
3673ff48bf5SDavid du Colombier 	/*
368*fe853e23SDavid du Colombier 	 *  favicon can be overridden by hostname.ico
369*fe853e23SDavid du Colombier 	 */
370*fe853e23SDavid du Colombier 	if(strcmp(c->req.uri, "/favicon.ico") == 0){
371*fe853e23SDavid du Colombier 		w2 = halloc(c, n+strlen(c->head.host)+2);
372*fe853e23SDavid du Colombier 		strcpy(w2, webroot);
373*fe853e23SDavid du Colombier 		strcat(w2, masque);
374*fe853e23SDavid du Colombier 		strcat(w2, "/");
375*fe853e23SDavid du Colombier 		strcat(w2, c->head.host);
376*fe853e23SDavid du Colombier 		strcat(w2, ".ico");
377*fe853e23SDavid du Colombier 		if(access(w2, AREAD)==0)
378*fe853e23SDavid du Colombier 			w = w2;
379*fe853e23SDavid du Colombier 	}
380*fe853e23SDavid du Colombier 
381*fe853e23SDavid du Colombier 	/*
3823ff48bf5SDavid du Colombier 	 * don't show the contents of .httplogin
3833ff48bf5SDavid du Colombier 	 */
3843ff48bf5SDavid du Colombier 	n = strlen(w);
3853ff48bf5SDavid du Colombier 	if(strcmp(w+n-STRLEN(".httplogin"), ".httplogin") == 0)
3863ff48bf5SDavid du Colombier 		return notfound(c, c->req.uri);
3873ff48bf5SDavid du Colombier 
3889a747e4fSDavid du Colombier 	fd = open(w, OREAD);
3899a747e4fSDavid du Colombier 	if(fd < 0 && strlen(masque)>0 && strncmp(c->req.uri, masque, strlen(masque)) == 0){
3909a747e4fSDavid du Colombier 		// may be a URI from before virtual hosts;  try again without masque
3919a747e4fSDavid du Colombier 		strcpy(w, webroot);
3927dd7cddfSDavid du Colombier 		strcat(w, c->req.uri);
3937dd7cddfSDavid du Colombier 		fd = open(w, OREAD);
3949a747e4fSDavid du Colombier 	}
3957dd7cddfSDavid du Colombier 	if(fd < 0)
3967dd7cddfSDavid du Colombier 		return notfound(c, c->req.uri);
3979a747e4fSDavid du Colombier 	dir = dirfstat(fd);
3989a747e4fSDavid du Colombier 	if(dir == nil){
3997dd7cddfSDavid du Colombier 		close(fd);
40080ee5cbfSDavid du Colombier 		return hfail(c, HInternal);
4017dd7cddfSDavid du Colombier 	}
4027dd7cddfSDavid du Colombier 	p = strchr(w, '\0');
4039a747e4fSDavid du Colombier 	if(dir->mode & DMDIR){
4049a747e4fSDavid du Colombier 		free(dir);
4057dd7cddfSDavid du Colombier 		if(p > w && p[-1] == '/'){
4067dd7cddfSDavid du Colombier 			strcat(w, "index.html");
4077dd7cddfSDavid du Colombier 			force301 = 0;
4087dd7cddfSDavid du Colombier 		}else{
4097dd7cddfSDavid du Colombier 			strcat(w, "/index.html");
4107dd7cddfSDavid du Colombier 			force301 = 1;
4117dd7cddfSDavid du Colombier 		}
4127dd7cddfSDavid du Colombier 		fd1 = open(w, OREAD);
4137dd7cddfSDavid du Colombier 		if(fd1 < 0){
4147dd7cddfSDavid du Colombier 			close(fd);
4157dd7cddfSDavid du Colombier 			return notfound(c, c->req.uri);
4167dd7cddfSDavid du Colombier 		}
4179a747e4fSDavid du Colombier 		c->req.uri = w + strlen(webroot) + strlen(masque);
4187dd7cddfSDavid du Colombier 		if(force301 && c->req.vermaj){
4197dd7cddfSDavid du Colombier 			close(fd);
4207dd7cddfSDavid du Colombier 			close(fd1);
42180ee5cbfSDavid du Colombier 			return hmoved(c, c->req.uri);
4227dd7cddfSDavid du Colombier 		}
4237dd7cddfSDavid du Colombier 		close(fd);
4247dd7cddfSDavid du Colombier 		fd = fd1;
4259a747e4fSDavid du Colombier 		dir = dirfstat(fd);
4269a747e4fSDavid du Colombier 		if(dir == nil){
4277dd7cddfSDavid du Colombier 			close(fd);
42880ee5cbfSDavid du Colombier 			return hfail(c, HInternal);
4297dd7cddfSDavid du Colombier 		}
4307dd7cddfSDavid du Colombier 	}else if(p > w && p[-1] == '/'){
4319a747e4fSDavid du Colombier 		free(dir);
4327dd7cddfSDavid du Colombier 		close(fd);
4337dd7cddfSDavid du Colombier 		*strrchr(c->req.uri, '/') = '\0';
43480ee5cbfSDavid du Colombier 		return hmoved(c, c->req.uri);
4357dd7cddfSDavid du Colombier 	}
4367dd7cddfSDavid du Colombier 
4379a747e4fSDavid du Colombier 	ok = authorize(c, w);
4389a747e4fSDavid du Colombier 	if(ok <= 0){
4399a747e4fSDavid du Colombier 		free(dir);
4409a747e4fSDavid du Colombier 		close(fd);
4419a747e4fSDavid du Colombier 		return ok;
4429a747e4fSDavid du Colombier 	}
4439a747e4fSDavid du Colombier 
4449a747e4fSDavid du Colombier 	return sendfd(c, fd, dir, nil, nil);
4457dd7cddfSDavid du Colombier }
4467dd7cddfSDavid du Colombier 
4477dd7cddfSDavid du Colombier static Strings
44880ee5cbfSDavid du Colombier stripmagic(HConnect *hc, char *uri)
4497dd7cddfSDavid du Colombier {
4507dd7cddfSDavid du Colombier 	Strings ss;
4517dd7cddfSDavid du Colombier 	char *newuri, *prog, *s;
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	prog = stripprefix("/magic/", uri);
4547dd7cddfSDavid du Colombier 	if(prog == nil){
4557dd7cddfSDavid du Colombier 		ss.s1 = uri;
4567dd7cddfSDavid du Colombier 		ss.s2 = nil;
4577dd7cddfSDavid du Colombier 		return ss;
4587dd7cddfSDavid du Colombier 	}
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier 	s = strchr(prog, '/');
4617dd7cddfSDavid du Colombier 	if(s == nil)
4627dd7cddfSDavid du Colombier 		newuri = "";
4637dd7cddfSDavid du Colombier 	else{
46480ee5cbfSDavid du Colombier 		newuri = hstrdup(hc, s);
4657dd7cddfSDavid du Colombier 		*s = 0;
4667dd7cddfSDavid du Colombier 		s = strrchr(s, '/');
4677dd7cddfSDavid du Colombier 		if(s != nil && s[1] == 0)
4687dd7cddfSDavid du Colombier 			*s = 0;
4697dd7cddfSDavid du Colombier 	}
4707dd7cddfSDavid du Colombier 	ss.s1 = newuri;
4717dd7cddfSDavid du Colombier 	ss.s2 = prog;
4727dd7cddfSDavid du Colombier 	return ss;
4737dd7cddfSDavid du Colombier }
4747dd7cddfSDavid du Colombier 
4757dd7cddfSDavid du Colombier static char*
4767dd7cddfSDavid du Colombier stripprefix(char *pre, char *str)
4777dd7cddfSDavid du Colombier {
4787dd7cddfSDavid du Colombier 	while(*pre)
4797dd7cddfSDavid du Colombier 		if(*str++ != *pre++)
4807dd7cddfSDavid du Colombier 			return nil;
4817dd7cddfSDavid du Colombier 	return str;
4827dd7cddfSDavid du Colombier }
4837dd7cddfSDavid du Colombier 
4849a747e4fSDavid du Colombier /*
4859a747e4fSDavid du Colombier  * couldn't open a file
4869a747e4fSDavid du Colombier  * figure out why and return and error message
4879a747e4fSDavid du Colombier  */
4889a747e4fSDavid du Colombier static int
4899a747e4fSDavid du Colombier notfound(HConnect *c, char *url)
4909a747e4fSDavid du Colombier {
4919a747e4fSDavid du Colombier 	c->xferbuf[0] = 0;
4929a747e4fSDavid du Colombier 	errstr(c->xferbuf, sizeof c->xferbuf);
4939a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "file does not exist") != nil)
4949a747e4fSDavid du Colombier 		return hfail(c, HNotFound, url);
4959a747e4fSDavid du Colombier 	if(strstr(c->xferbuf, "permission denied") != nil)
4969a747e4fSDavid du Colombier 		return hfail(c, HUnauth, url);
4979a747e4fSDavid du Colombier 	return hfail(c, HNotFound, url);
4989a747e4fSDavid du Colombier }
4999a747e4fSDavid du Colombier 
5007dd7cddfSDavid du Colombier static char*
5017dd7cddfSDavid du Colombier sysdom(void)
5027dd7cddfSDavid du Colombier {
5037dd7cddfSDavid du Colombier 	char *dn;
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier 	dn = csquery("sys" , sysname(), "dom");
5067dd7cddfSDavid du Colombier 	if(dn == nil)
5077dd7cddfSDavid du Colombier 		dn = "who cares";
5087dd7cddfSDavid du Colombier 	return dn;
5097dd7cddfSDavid du Colombier }
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier /*
5127dd7cddfSDavid du Colombier  *  query the connection server
5137dd7cddfSDavid du Colombier  */
5147dd7cddfSDavid du Colombier static char*
5157dd7cddfSDavid du Colombier csquery(char *attr, char *val, char *rattr)
5167dd7cddfSDavid du Colombier {
5177dd7cddfSDavid du Colombier 	char token[64+4];
5187dd7cddfSDavid du Colombier 	char buf[256], *p, *sp;
5197dd7cddfSDavid du Colombier 	int fd, n;
5207dd7cddfSDavid du Colombier 
5217dd7cddfSDavid du Colombier 	if(val == nil || val[0] == 0)
5227dd7cddfSDavid du Colombier 		return nil;
5237dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", netdir);
5247dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
5257dd7cddfSDavid du Colombier 	if(fd < 0)
5267dd7cddfSDavid du Colombier 		return nil;
5277dd7cddfSDavid du Colombier 	fprint(fd, "!%s=%s", attr, val);
5287dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
5297dd7cddfSDavid du Colombier 	snprint(token, sizeof(token), "%s=", rattr);
5307dd7cddfSDavid du Colombier 	for(;;){
5317dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf)-1);
5327dd7cddfSDavid du Colombier 		if(n <= 0)
5337dd7cddfSDavid du Colombier 			break;
5347dd7cddfSDavid du Colombier 		buf[n] = 0;
5357dd7cddfSDavid du Colombier 		p = strstr(buf, token);
5367dd7cddfSDavid du Colombier 		if(p != nil && (p == buf || *(p-1) == 0)){
5377dd7cddfSDavid du Colombier 			close(fd);
5387dd7cddfSDavid du Colombier 			sp = strchr(p, ' ');
5397dd7cddfSDavid du Colombier 			if(sp)
5407dd7cddfSDavid du Colombier 				*sp = 0;
5417dd7cddfSDavid du Colombier 			p = strchr(p, '=');
5427dd7cddfSDavid du Colombier 			if(p == nil)
5437dd7cddfSDavid du Colombier 				return nil;
5447dd7cddfSDavid du Colombier 			return estrdup(p+1);
5457dd7cddfSDavid du Colombier 		}
5467dd7cddfSDavid du Colombier 	}
5477dd7cddfSDavid du Colombier 	close(fd);
5487dd7cddfSDavid du Colombier 	return nil;
5497dd7cddfSDavid du Colombier }
550