xref: /plan9/sys/src/cmd/ip/httpd/httpd.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include <u.h>
2*7dd7cddfSDavid du Colombier #include <libc.h>
3*7dd7cddfSDavid du Colombier #include <auth.h>
4*7dd7cddfSDavid du Colombier #include "httpd.h"
5*7dd7cddfSDavid du Colombier #include "can.h"
6*7dd7cddfSDavid du Colombier 
7*7dd7cddfSDavid du Colombier typedef struct Endpoints	Endpoints;
8*7dd7cddfSDavid du Colombier typedef struct Strings		Strings;
9*7dd7cddfSDavid du Colombier 
10*7dd7cddfSDavid du Colombier struct Endpoints
11*7dd7cddfSDavid du Colombier {
12*7dd7cddfSDavid du Colombier 	char	*lsys;
13*7dd7cddfSDavid du Colombier 	char	*lserv;
14*7dd7cddfSDavid du Colombier 	char	*rsys;
15*7dd7cddfSDavid du Colombier 	char	*rserv;
16*7dd7cddfSDavid du Colombier };
17*7dd7cddfSDavid du Colombier 
18*7dd7cddfSDavid du Colombier struct Strings
19*7dd7cddfSDavid du Colombier {
20*7dd7cddfSDavid du Colombier 	char	*s1;
21*7dd7cddfSDavid du Colombier 	char	*s2;
22*7dd7cddfSDavid du Colombier };
23*7dd7cddfSDavid du Colombier 
24*7dd7cddfSDavid du Colombier /*
25*7dd7cddfSDavid du Colombier  * import from parse.c
26*7dd7cddfSDavid du Colombier  */
27*7dd7cddfSDavid du Colombier extern	Can		*httpcan;
28*7dd7cddfSDavid du Colombier 
29*7dd7cddfSDavid du Colombier static	char*		abspath(char*, char*);
30*7dd7cddfSDavid du Colombier static	void		becomenone(char*);
31*7dd7cddfSDavid du Colombier static	char		*csquery(char*, char*, char*);
32*7dd7cddfSDavid du Colombier static	void		dolisten(char*);
33*7dd7cddfSDavid du Colombier static	int		getc(Connect*);
34*7dd7cddfSDavid du Colombier static	char*		getword(Connect*);
35*7dd7cddfSDavid du Colombier static	Strings		parseuri(char*);
36*7dd7cddfSDavid du Colombier static	int		send(Connect*);
37*7dd7cddfSDavid du Colombier static	Strings		stripmagic(char*);
38*7dd7cddfSDavid du Colombier static	char*		stripprefix(char*, char*);
39*7dd7cddfSDavid du Colombier static	Strings		stripsearch(char*);
40*7dd7cddfSDavid du Colombier static	char*		sysdom(void);
41*7dd7cddfSDavid du Colombier static	Endpoints*	getendpoints(char*);
42*7dd7cddfSDavid du Colombier static	int		parsereq(Connect *c);
43*7dd7cddfSDavid du Colombier 
44*7dd7cddfSDavid du Colombier /*
45*7dd7cddfSDavid du Colombier  * these should be done better; see the reponse codes in /lib/rfc/rfc2616 for
46*7dd7cddfSDavid du Colombier  * more info on what should be included.
47*7dd7cddfSDavid du Colombier  */
48*7dd7cddfSDavid du Colombier #define UNAUTHED	"You are not authorized to see this area.\n"
49*7dd7cddfSDavid du Colombier #define NOCONTENT	"No acceptable type of data is available.\n"
50*7dd7cddfSDavid du Colombier #define NOENCODE	"No acceptable encoding of the contents is available.\n"
51*7dd7cddfSDavid du Colombier #define UNMATCHED	"The entity requested does not match the existing entity.\n"
52*7dd7cddfSDavid du Colombier #define BADRANGE	"No bytes are avaible for the range you requested.\n"
53*7dd7cddfSDavid du Colombier 
54*7dd7cddfSDavid du Colombier void
55*7dd7cddfSDavid du Colombier usage(void)
56*7dd7cddfSDavid du Colombier {
57*7dd7cddfSDavid du Colombier 	fprint(2, "usage: httpd [-a srvaddress] [-d domain] [-n namespace] [-w webroot]\n");
58*7dd7cddfSDavid du Colombier 	exits("usage");
59*7dd7cddfSDavid du Colombier }
60*7dd7cddfSDavid du Colombier 
61*7dd7cddfSDavid du Colombier void
62*7dd7cddfSDavid du Colombier main(int argc, char **argv)
63*7dd7cddfSDavid du Colombier {
64*7dd7cddfSDavid du Colombier 	char *address;
65*7dd7cddfSDavid du Colombier 
66*7dd7cddfSDavid du Colombier 	namespace = nil;
67*7dd7cddfSDavid du Colombier 	address = nil;
68*7dd7cddfSDavid du Colombier 	mydomain = nil;
69*7dd7cddfSDavid du Colombier 	strcpy(netdir, "/net");
70*7dd7cddfSDavid du Colombier 	fmtinstall('D', dateconv);
71*7dd7cddfSDavid du Colombier 	fmtinstall('H', httpconv);
72*7dd7cddfSDavid du Colombier 	fmtinstall('U', urlconv);
73*7dd7cddfSDavid du Colombier 	ARGBEGIN{
74*7dd7cddfSDavid du Colombier 	case 'n':
75*7dd7cddfSDavid du Colombier 		namespace = ARGF();
76*7dd7cddfSDavid du Colombier 		break;
77*7dd7cddfSDavid du Colombier 	case 'a':
78*7dd7cddfSDavid du Colombier 		address = ARGF();
79*7dd7cddfSDavid du Colombier 		break;
80*7dd7cddfSDavid du Colombier 	case 'd':
81*7dd7cddfSDavid du Colombier 		mydomain = ARGF();
82*7dd7cddfSDavid du Colombier 		break;
83*7dd7cddfSDavid du Colombier 	case 'w':
84*7dd7cddfSDavid du Colombier 		webroot = ARGF();
85*7dd7cddfSDavid du Colombier 		break;
86*7dd7cddfSDavid du Colombier 	default:
87*7dd7cddfSDavid du Colombier 		usage();
88*7dd7cddfSDavid du Colombier 		break;
89*7dd7cddfSDavid du Colombier 	}ARGEND
90*7dd7cddfSDavid du Colombier 
91*7dd7cddfSDavid du Colombier 	if(argc)
92*7dd7cddfSDavid du Colombier 		usage();
93*7dd7cddfSDavid du Colombier 
94*7dd7cddfSDavid du Colombier 	if(namespace == nil)
95*7dd7cddfSDavid du Colombier 		namespace = "/lib/namespace.httpd";
96*7dd7cddfSDavid du Colombier 	if(address == nil)
97*7dd7cddfSDavid du Colombier 		address = "tcp!*!http";
98*7dd7cddfSDavid du Colombier 	if(webroot == nil)
99*7dd7cddfSDavid du Colombier 		webroot = "/usr/web";
100*7dd7cddfSDavid du Colombier 	else{
101*7dd7cddfSDavid du Colombier 		cleanname(webroot);
102*7dd7cddfSDavid du Colombier 		if(webroot[0] != '/')
103*7dd7cddfSDavid du Colombier 			webroot = "/usr/web";
104*7dd7cddfSDavid du Colombier 	}
105*7dd7cddfSDavid du Colombier 
106*7dd7cddfSDavid du Colombier 	switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNAMEG)) {
107*7dd7cddfSDavid du Colombier 	case -1:
108*7dd7cddfSDavid du Colombier 		sysfatal("fork");
109*7dd7cddfSDavid du Colombier 	case 0:
110*7dd7cddfSDavid du Colombier 		break;
111*7dd7cddfSDavid du Colombier 	default:
112*7dd7cddfSDavid du Colombier 		exits(nil);
113*7dd7cddfSDavid du Colombier 	}
114*7dd7cddfSDavid du Colombier 
115*7dd7cddfSDavid du Colombier 	/*
116*7dd7cddfSDavid du Colombier 	 * open all files we might need before castrating namespace
117*7dd7cddfSDavid du Colombier 	 */
118*7dd7cddfSDavid du Colombier 	time(nil);
119*7dd7cddfSDavid du Colombier 	if(mydomain == nil)
120*7dd7cddfSDavid du Colombier 		mydomain = sysdom();
121*7dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, nil);
122*7dd7cddfSDavid du Colombier 	logall[0] = open("/sys/log/httpd/0",OWRITE);
123*7dd7cddfSDavid du Colombier 	logall[1] = open("/sys/log/httpd/1",OWRITE);
124*7dd7cddfSDavid du Colombier 	redirectinit();
125*7dd7cddfSDavid du Colombier 	contentinit();
126*7dd7cddfSDavid du Colombier 	urlinit();
127*7dd7cddfSDavid du Colombier 	statsinit();
128*7dd7cddfSDavid du Colombier 
129*7dd7cddfSDavid du Colombier 	becomenone(namespace);
130*7dd7cddfSDavid du Colombier 	dolisten(address);
131*7dd7cddfSDavid du Colombier 	exits(nil);
132*7dd7cddfSDavid du Colombier }
133*7dd7cddfSDavid du Colombier 
134*7dd7cddfSDavid du Colombier static void
135*7dd7cddfSDavid du Colombier becomenone(char *namespace)
136*7dd7cddfSDavid du Colombier {
137*7dd7cddfSDavid du Colombier 	int fd;
138*7dd7cddfSDavid du Colombier 
139*7dd7cddfSDavid du Colombier 	fd = open("#c/user", OWRITE);
140*7dd7cddfSDavid du Colombier 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
141*7dd7cddfSDavid du Colombier 		sysfatal("can't become none");
142*7dd7cddfSDavid du Colombier 	close(fd);
143*7dd7cddfSDavid du Colombier 	if(newns("none", nil) < 0)
144*7dd7cddfSDavid du Colombier 		sysfatal("can't build normal namespace");
145*7dd7cddfSDavid du Colombier 	if(addns("none", namespace) < 0)
146*7dd7cddfSDavid du Colombier 		sysfatal("can't build httpd namespace");
147*7dd7cddfSDavid du Colombier }
148*7dd7cddfSDavid du Colombier 
149*7dd7cddfSDavid du Colombier static Connect*
150*7dd7cddfSDavid du Colombier mkconnect(void)
151*7dd7cddfSDavid du Colombier {
152*7dd7cddfSDavid du Colombier 	Connect *c;
153*7dd7cddfSDavid du Colombier 
154*7dd7cddfSDavid du Colombier 	c = ezalloc(sizeof(Connect));
155*7dd7cddfSDavid du Colombier 	c->hpos = c->header;
156*7dd7cddfSDavid du Colombier 	c->hstop = c->header;
157*7dd7cddfSDavid du Colombier 	return c;
158*7dd7cddfSDavid du Colombier }
159*7dd7cddfSDavid du Colombier 
160*7dd7cddfSDavid du Colombier static void
161*7dd7cddfSDavid du Colombier dolisten(char *address)
162*7dd7cddfSDavid du Colombier {
163*7dd7cddfSDavid du Colombier 	Connect *c;
164*7dd7cddfSDavid du Colombier 	Endpoints *ends;
165*7dd7cddfSDavid du Colombier 	char ndir[NETPATHLEN], dir[NETPATHLEN], *p;
166*7dd7cddfSDavid du Colombier 	int ctl, nctl, data;
167*7dd7cddfSDavid du Colombier 	int spotchk = 0;
168*7dd7cddfSDavid du Colombier 
169*7dd7cddfSDavid du Colombier 	syslog(0, HTTPLOG, "httpd starting");
170*7dd7cddfSDavid du Colombier 	ctl = announce(address, dir);
171*7dd7cddfSDavid du Colombier 	if(ctl < 0){
172*7dd7cddfSDavid du Colombier 		syslog(0, HTTPLOG, "can't announce on %s: %r", address);
173*7dd7cddfSDavid du Colombier 		return;
174*7dd7cddfSDavid du Colombier 	}
175*7dd7cddfSDavid du Colombier 	strcpy(netdir, dir);
176*7dd7cddfSDavid du Colombier 	p = nil;
177*7dd7cddfSDavid du Colombier 	if(netdir[0] == '/'){
178*7dd7cddfSDavid du Colombier 		p = strchr(netdir+1, '/');
179*7dd7cddfSDavid du Colombier 		if(p != nil)
180*7dd7cddfSDavid du Colombier 			*p = '\0';
181*7dd7cddfSDavid du Colombier 	}
182*7dd7cddfSDavid du Colombier 	if(p == nil)
183*7dd7cddfSDavid du Colombier 		strcpy(netdir, "/net");
184*7dd7cddfSDavid du Colombier 
185*7dd7cddfSDavid du Colombier 	for(;;){
186*7dd7cddfSDavid du Colombier 
187*7dd7cddfSDavid du Colombier 		/*
188*7dd7cddfSDavid du Colombier 		 *  wait for a call (or an error)
189*7dd7cddfSDavid du Colombier 		 */
190*7dd7cddfSDavid du Colombier 		nctl = listen(dir, ndir);
191*7dd7cddfSDavid du Colombier 		if(nctl < 0){
192*7dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "can't listen on %s: %r", address);
193*7dd7cddfSDavid du Colombier 			syslog(0, HTTPLOG, "ctls = %d", ctl);
194*7dd7cddfSDavid du Colombier 			for(;;)sleep(1000);
195*7dd7cddfSDavid du Colombier 			return;
196*7dd7cddfSDavid du Colombier 		}
197*7dd7cddfSDavid du Colombier 
198*7dd7cddfSDavid du Colombier 		/*
199*7dd7cddfSDavid du Colombier 		 *  start a process for the service
200*7dd7cddfSDavid du Colombier 		 */
201*7dd7cddfSDavid du Colombier 		switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG)){
202*7dd7cddfSDavid du Colombier 		case -1:
203*7dd7cddfSDavid du Colombier 			close(nctl);
204*7dd7cddfSDavid du Colombier 			continue;
205*7dd7cddfSDavid du Colombier 		case 0:
206*7dd7cddfSDavid du Colombier 			/*
207*7dd7cddfSDavid du Colombier 			 *  see if we know the service requested
208*7dd7cddfSDavid du Colombier 			 */
209*7dd7cddfSDavid du Colombier 			data = accept(ctl, ndir);
210*7dd7cddfSDavid du Colombier 			if(data < 0){
211*7dd7cddfSDavid du Colombier 				syslog(0, HTTPLOG, "can't open %s/data: %r", ndir);
212*7dd7cddfSDavid du Colombier 				exits(nil);
213*7dd7cddfSDavid du Colombier 			}
214*7dd7cddfSDavid du Colombier 			dup(data, 0);
215*7dd7cddfSDavid du Colombier 			dup(data, 1);
216*7dd7cddfSDavid du Colombier 			dup(data, 2);
217*7dd7cddfSDavid du Colombier 			close(data);
218*7dd7cddfSDavid du Colombier 			close(ctl);
219*7dd7cddfSDavid du Colombier 			close(nctl);
220*7dd7cddfSDavid du Colombier 
221*7dd7cddfSDavid du Colombier 			ends = getendpoints(ndir);
222*7dd7cddfSDavid du Colombier 			c = mkconnect();
223*7dd7cddfSDavid du Colombier 			c->remotesys = ends->rsys;
224*7dd7cddfSDavid du Colombier 			c->begin_time = time(nil);
225*7dd7cddfSDavid du Colombier 
226*7dd7cddfSDavid du Colombier 			hinit(&c->hin, 0, Hread);
227*7dd7cddfSDavid du Colombier 			hinit(&c->hout, 1, Hwrite);
228*7dd7cddfSDavid du Colombier 
229*7dd7cddfSDavid du Colombier 			parsereq(c);
230*7dd7cddfSDavid du Colombier 
231*7dd7cddfSDavid du Colombier 			exits(nil);
232*7dd7cddfSDavid du Colombier 		default:
233*7dd7cddfSDavid du Colombier 			close(nctl);
234*7dd7cddfSDavid du Colombier 			break;
235*7dd7cddfSDavid du Colombier 		}
236*7dd7cddfSDavid du Colombier 
237*7dd7cddfSDavid du Colombier 		if(++spotchk > 50){
238*7dd7cddfSDavid du Colombier 			spotchk = 0;
239*7dd7cddfSDavid du Colombier 			redirectinit();
240*7dd7cddfSDavid du Colombier 			contentinit();
241*7dd7cddfSDavid du Colombier 			urlinit();
242*7dd7cddfSDavid du Colombier 			statsinit();
243*7dd7cddfSDavid du Colombier 		}
244*7dd7cddfSDavid du Colombier 	}
245*7dd7cddfSDavid du Colombier }
246*7dd7cddfSDavid du Colombier 
247*7dd7cddfSDavid du Colombier static int
248*7dd7cddfSDavid du Colombier parsereq(Connect *c)
249*7dd7cddfSDavid du Colombier {
250*7dd7cddfSDavid du Colombier 	Strings ss;
251*7dd7cddfSDavid du Colombier 	char *vs, *v, *magic, *search, *uri, *origuri, *extra, *newpath;
252*7dd7cddfSDavid du Colombier 	char virtualhost[100];
253*7dd7cddfSDavid du Colombier 	int t, n, ok;
254*7dd7cddfSDavid du Colombier 
255*7dd7cddfSDavid du Colombier 	if(httpcan != nil){
256*7dd7cddfSDavid du Colombier 		logit(c, "starting request with request data allocated");
257*7dd7cddfSDavid du Colombier 		fail(c, Internal);
258*7dd7cddfSDavid du Colombier 	}
259*7dd7cddfSDavid du Colombier 
260*7dd7cddfSDavid du Colombier 	/*
261*7dd7cddfSDavid du Colombier 	 * serve requests until a magic request.
262*7dd7cddfSDavid du Colombier 	 * later requests have to come quickly.
263*7dd7cddfSDavid du Colombier 	 * only works for http/1.1 or later.
264*7dd7cddfSDavid du Colombier 	 */
265*7dd7cddfSDavid du Colombier 	for(t = 15*60*1000; ; t = 15*1000){
266*7dd7cddfSDavid du Colombier 		alarm(t);
267*7dd7cddfSDavid du Colombier 		if(!gethead(c, 0))
268*7dd7cddfSDavid du Colombier 			return 0;
269*7dd7cddfSDavid du Colombier 		alarm(0);
270*7dd7cddfSDavid du Colombier 		c->req.meth = getword(c);
271*7dd7cddfSDavid du Colombier 		if(c->req.meth == nil)
272*7dd7cddfSDavid du Colombier 			return fail(c, Syntax);
273*7dd7cddfSDavid du Colombier 		uri = getword(c);
274*7dd7cddfSDavid du Colombier 		if(uri == nil || strlen(uri) == 0)
275*7dd7cddfSDavid du Colombier 			return fail(c, Syntax);
276*7dd7cddfSDavid du Colombier 		v = getword(c);
277*7dd7cddfSDavid du Colombier 		if(v == nil){
278*7dd7cddfSDavid du Colombier 			if(strcmp(c->req.meth, "GET") != 0)
279*7dd7cddfSDavid du Colombier 				return fail(c, Unimp, c->req.meth);
280*7dd7cddfSDavid du Colombier 			c->req.vermaj = 0;
281*7dd7cddfSDavid du Colombier 			c->req.vermin = 9;
282*7dd7cddfSDavid du Colombier 		}else{
283*7dd7cddfSDavid du Colombier 			vs = v;
284*7dd7cddfSDavid du Colombier 			if(strncmp(vs, "HTTP/", 5) != 0)
285*7dd7cddfSDavid du Colombier 				return fail(c, UnkVers, vs);
286*7dd7cddfSDavid du Colombier 			vs += 5;
287*7dd7cddfSDavid du Colombier 			c->req.vermaj = strtoul(vs, &vs, 10);
288*7dd7cddfSDavid du Colombier 			if(*vs != '.' || c->req.vermaj != 1)
289*7dd7cddfSDavid du Colombier 				return fail(c, UnkVers, vs);
290*7dd7cddfSDavid du Colombier 			vs++;
291*7dd7cddfSDavid du Colombier 			c->req.vermin = strtoul(vs, &vs, 10);
292*7dd7cddfSDavid du Colombier 			if(*vs != '\0')
293*7dd7cddfSDavid du Colombier 				return fail(c, UnkVers, vs);
294*7dd7cddfSDavid du Colombier 
295*7dd7cddfSDavid du Colombier 			extra = getword(c);
296*7dd7cddfSDavid du Colombier 			if(extra != nil)
297*7dd7cddfSDavid du Colombier 				return fail(c, Syntax);
298*7dd7cddfSDavid du Colombier 		}
299*7dd7cddfSDavid du Colombier 
300*7dd7cddfSDavid du Colombier 		/*
301*7dd7cddfSDavid du Colombier 		 * the fragment is not supposed to be sent
302*7dd7cddfSDavid du Colombier 		 * strip it 'cause some clients send it
303*7dd7cddfSDavid du Colombier 		 */
304*7dd7cddfSDavid du Colombier 		origuri = uri;
305*7dd7cddfSDavid du Colombier 		uri = strchr(origuri, '#');
306*7dd7cddfSDavid du Colombier 		if(uri != nil)
307*7dd7cddfSDavid du Colombier 			*uri = 0;
308*7dd7cddfSDavid du Colombier 
309*7dd7cddfSDavid du Colombier 		/*
310*7dd7cddfSDavid du Colombier 		 * http/1.1 requires the server to accept absolute
311*7dd7cddfSDavid du Colombier 		 * or relative uri's.  convert to relative with an absolute path
312*7dd7cddfSDavid du Colombier 		 */
313*7dd7cddfSDavid du Colombier 		if(http11(c)){
314*7dd7cddfSDavid du Colombier 			ss = parseuri(origuri);
315*7dd7cddfSDavid du Colombier 			uri = ss.s1;
316*7dd7cddfSDavid du Colombier 			c->req.urihost = ss.s2;
317*7dd7cddfSDavid du Colombier 			if(uri == nil)
318*7dd7cddfSDavid du Colombier 				return fail(c, BadReq, uri);
319*7dd7cddfSDavid du Colombier 			origuri = uri;
320*7dd7cddfSDavid du Colombier 		}
321*7dd7cddfSDavid du Colombier 
322*7dd7cddfSDavid du Colombier 		/*
323*7dd7cddfSDavid du Colombier 		 * munge uri for search, protection, and magic
324*7dd7cddfSDavid du Colombier 		 */
325*7dd7cddfSDavid du Colombier 		ss = stripsearch(origuri);
326*7dd7cddfSDavid du Colombier 		origuri = ss.s1;
327*7dd7cddfSDavid du Colombier 		search = ss.s2;
328*7dd7cddfSDavid du Colombier 		uri = urlunesc(origuri);
329*7dd7cddfSDavid du Colombier 		uri = abspath(uri, "/");
330*7dd7cddfSDavid du Colombier 		if(uri == nil || uri[0] == 0)
331*7dd7cddfSDavid du Colombier 			return fail(c, NotFound, "no object specified");
332*7dd7cddfSDavid du Colombier 		ss = stripmagic(uri);
333*7dd7cddfSDavid du Colombier 		uri = ss.s1;
334*7dd7cddfSDavid du Colombier 		magic = ss.s2;
335*7dd7cddfSDavid du Colombier 
336*7dd7cddfSDavid du Colombier 		c->req.uri = uri;
337*7dd7cddfSDavid du Colombier 		c->req.search = search;
338*7dd7cddfSDavid du Colombier 		if(magic != nil && strcmp(magic, "httpd") != 0)
339*7dd7cddfSDavid du Colombier 			break;
340*7dd7cddfSDavid du Colombier 
341*7dd7cddfSDavid du Colombier 		/*
342*7dd7cddfSDavid du Colombier 		 * normal case is just file transfer
343*7dd7cddfSDavid du Colombier 		 */
344*7dd7cddfSDavid du Colombier 		origuri = uri;
345*7dd7cddfSDavid du Colombier 		httpheaders(c);
346*7dd7cddfSDavid du Colombier 		if(!http11(c) && !c->head.persist)
347*7dd7cddfSDavid du Colombier 			c->head.closeit = 1;
348*7dd7cddfSDavid du Colombier 
349*7dd7cddfSDavid du Colombier 		if(origuri[0]=='/' && origuri[1]=='~'){
350*7dd7cddfSDavid du Colombier 			n = strlen(origuri) + 4 + UTFmax;
351*7dd7cddfSDavid du Colombier 			newpath = halloc(n);
352*7dd7cddfSDavid du Colombier 			snprint(newpath, n, "/who/%s", origuri+2);
353*7dd7cddfSDavid du Colombier 			c->req.uri = newpath;
354*7dd7cddfSDavid du Colombier 			uri = newpath;
355*7dd7cddfSDavid du Colombier 		}else if(origuri[0]=='/' && origuri[1]==0){
356*7dd7cddfSDavid du Colombier 			snprint(virtualhost, sizeof virtualhost, "http://%s/", c->head.host);
357*7dd7cddfSDavid du Colombier 			uri = redirect(virtualhost);
358*7dd7cddfSDavid du Colombier 			if(uri == nil)
359*7dd7cddfSDavid du Colombier 				uri = redirect(origuri);
360*7dd7cddfSDavid du Colombier 		}else
361*7dd7cddfSDavid du Colombier 			uri = redirect(origuri);
362*7dd7cddfSDavid du Colombier 		if(uri == nil)
363*7dd7cddfSDavid du Colombier 			ok = send(c);
364*7dd7cddfSDavid du Colombier 		else
365*7dd7cddfSDavid du Colombier 			ok = moved(c, uri);
366*7dd7cddfSDavid du Colombier 
367*7dd7cddfSDavid du Colombier 		if(c->head.closeit || ok < 0)
368*7dd7cddfSDavid du Colombier 			exits(nil);
369*7dd7cddfSDavid du Colombier 
370*7dd7cddfSDavid du Colombier 		/*
371*7dd7cddfSDavid du Colombier 		 * clean up all of the junk from that request
372*7dd7cddfSDavid du Colombier 		 */
373*7dd7cddfSDavid du Colombier 		hxferenc(&c->hout, 0);
374*7dd7cddfSDavid du Colombier 		hflush(&c->hout);
375*7dd7cddfSDavid du Colombier 		reqcleanup(c);
376*7dd7cddfSDavid du Colombier 	}
377*7dd7cddfSDavid du Colombier 
378*7dd7cddfSDavid du Colombier 	/*
379*7dd7cddfSDavid du Colombier 	 * for magic we exec a new program and serve no more requests
380*7dd7cddfSDavid du Colombier 	 */
381*7dd7cddfSDavid du Colombier 	snprint(c->xferbuf, BufSize, "/bin/ip/httpd/%s", magic);
382*7dd7cddfSDavid du Colombier 	writelog(c, "Magic: %s\n", magic);
383*7dd7cddfSDavid du Colombier 	execl(c->xferbuf, magic, "-d", mydomain, "-w", webroot, "-r", c->remotesys, "-N", netdir, "-b", hunload(&c->hin),
384*7dd7cddfSDavid du Colombier 		c->req.meth, v, uri, search, nil);
385*7dd7cddfSDavid du Colombier 	logit(c, "no magic %s uri %s", magic, uri);
386*7dd7cddfSDavid du Colombier 	return fail(c, NotFound, origuri);
387*7dd7cddfSDavid du Colombier }
388*7dd7cddfSDavid du Colombier 
389*7dd7cddfSDavid du Colombier static Strings
390*7dd7cddfSDavid du Colombier parseuri(char *uri)
391*7dd7cddfSDavid du Colombier {
392*7dd7cddfSDavid du Colombier 	Strings ss;
393*7dd7cddfSDavid du Colombier 	char *urihost, *p;
394*7dd7cddfSDavid du Colombier 
395*7dd7cddfSDavid du Colombier 	urihost = nil;
396*7dd7cddfSDavid du Colombier 	if(uri[0] != '/'){
397*7dd7cddfSDavid du Colombier 		if(cistrncmp(uri, "http://", 7) != 0){
398*7dd7cddfSDavid du Colombier 			ss.s1 = nil;
399*7dd7cddfSDavid du Colombier 			ss.s2 = nil;
400*7dd7cddfSDavid du Colombier 			return ss;
401*7dd7cddfSDavid du Colombier 		}
402*7dd7cddfSDavid du Colombier 		uri += 5;	/* skip http: */
403*7dd7cddfSDavid du Colombier 	}
404*7dd7cddfSDavid du Colombier 
405*7dd7cddfSDavid du Colombier 	/*
406*7dd7cddfSDavid du Colombier 	 * anything starting with // is a host name or number
407*7dd7cddfSDavid du Colombier 	 * hostnames constists of letters, digits, - and .
408*7dd7cddfSDavid du Colombier 	 * for now, just ignore any port given
409*7dd7cddfSDavid du Colombier 	 */
410*7dd7cddfSDavid du Colombier 	if(uri[0] == '/' && uri[1] == '/'){
411*7dd7cddfSDavid du Colombier 		urihost = uri + 2;
412*7dd7cddfSDavid du Colombier 		p = strchr(urihost, '/');
413*7dd7cddfSDavid du Colombier 		if(p == nil)
414*7dd7cddfSDavid du Colombier 			uri = hstrdup("/");
415*7dd7cddfSDavid du Colombier 		else{
416*7dd7cddfSDavid du Colombier 			uri = hstrdup(p);
417*7dd7cddfSDavid du Colombier 			*p = '\0';
418*7dd7cddfSDavid du Colombier 		}
419*7dd7cddfSDavid du Colombier 		p = strchr(urihost, ':');
420*7dd7cddfSDavid du Colombier 		if(p != nil)
421*7dd7cddfSDavid du Colombier 			*p = '\0';
422*7dd7cddfSDavid du Colombier 	}
423*7dd7cddfSDavid du Colombier 
424*7dd7cddfSDavid du Colombier 	if(uri[0] != '/' || uri[1] == '/'){
425*7dd7cddfSDavid du Colombier 		ss.s1 = nil;
426*7dd7cddfSDavid du Colombier 		ss.s2 = nil;
427*7dd7cddfSDavid du Colombier 		return ss;
428*7dd7cddfSDavid du Colombier 	}
429*7dd7cddfSDavid du Colombier 
430*7dd7cddfSDavid du Colombier 	ss.s1 = uri;
431*7dd7cddfSDavid du Colombier 	ss.s2 = lower(urihost);
432*7dd7cddfSDavid du Colombier 	return ss;
433*7dd7cddfSDavid du Colombier }
434*7dd7cddfSDavid du Colombier 
435*7dd7cddfSDavid du Colombier static int
436*7dd7cddfSDavid du Colombier send(Connect *c)
437*7dd7cddfSDavid du Colombier {
438*7dd7cddfSDavid du Colombier 	Dir dir;
439*7dd7cddfSDavid du Colombier 	char *w, *p;
440*7dd7cddfSDavid du Colombier 	int fd, fd1, n, force301, ok;
441*7dd7cddfSDavid du Colombier 
442*7dd7cddfSDavid du Colombier 	if(c->req.search)
443*7dd7cddfSDavid du Colombier 		return fail(c, NoSearch, c->req.uri);
444*7dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
445*7dd7cddfSDavid du Colombier 		return unallowed(c, "GET, HEAD");
446*7dd7cddfSDavid du Colombier 	if(c->head.expectother || c->head.expectcont)
447*7dd7cddfSDavid du Colombier 		return fail(c, ExpectFail);
448*7dd7cddfSDavid du Colombier 
449*7dd7cddfSDavid du Colombier 	ok = authcheck(c);
450*7dd7cddfSDavid du Colombier 	if(ok <= 0)
451*7dd7cddfSDavid du Colombier 		return ok;
452*7dd7cddfSDavid du Colombier 
453*7dd7cddfSDavid du Colombier 	/*
454*7dd7cddfSDavid du Colombier 	 * check for directory/file mismatch with trailing /,
455*7dd7cddfSDavid du Colombier 	 * and send any redirections.
456*7dd7cddfSDavid du Colombier 	 */
457*7dd7cddfSDavid du Colombier 	n = strlen(webroot) + strlen(c->req.uri) + STRLEN("/index.html") + 1;
458*7dd7cddfSDavid du Colombier 	w = halloc(n);
459*7dd7cddfSDavid du Colombier 	strcpy(w, webroot);
460*7dd7cddfSDavid du Colombier 	strcat(w, c->req.uri);
461*7dd7cddfSDavid du Colombier 	fd = open(w, OREAD);
462*7dd7cddfSDavid du Colombier 	if(fd < 0)
463*7dd7cddfSDavid du Colombier 		return notfound(c, c->req.uri);
464*7dd7cddfSDavid du Colombier 	if(dirfstat(fd, &dir) < 0){
465*7dd7cddfSDavid du Colombier 		close(fd);
466*7dd7cddfSDavid du Colombier 		return fail(c, Internal);
467*7dd7cddfSDavid du Colombier 	}
468*7dd7cddfSDavid du Colombier 	p = strchr(w, '\0');
469*7dd7cddfSDavid du Colombier 	if(dir.mode & CHDIR){
470*7dd7cddfSDavid du Colombier 		if(p > w && p[-1] == '/'){
471*7dd7cddfSDavid du Colombier 			strcat(w, "index.html");
472*7dd7cddfSDavid du Colombier 			force301 = 0;
473*7dd7cddfSDavid du Colombier 		}else{
474*7dd7cddfSDavid du Colombier 			strcat(w, "/index.html");
475*7dd7cddfSDavid du Colombier 			force301 = 1;
476*7dd7cddfSDavid du Colombier 		}
477*7dd7cddfSDavid du Colombier 		fd1 = open(w, OREAD);
478*7dd7cddfSDavid du Colombier 		if(fd1 < 0){
479*7dd7cddfSDavid du Colombier 			close(fd);
480*7dd7cddfSDavid du Colombier 			return notfound(c, c->req.uri);
481*7dd7cddfSDavid du Colombier 		}
482*7dd7cddfSDavid du Colombier 		c->req.uri = w + strlen(webroot);
483*7dd7cddfSDavid du Colombier 		if(force301 && c->req.vermaj){
484*7dd7cddfSDavid du Colombier 			close(fd);
485*7dd7cddfSDavid du Colombier 			close(fd1);
486*7dd7cddfSDavid du Colombier 			return moved(c, c->req.uri);
487*7dd7cddfSDavid du Colombier 		}
488*7dd7cddfSDavid du Colombier 		close(fd);
489*7dd7cddfSDavid du Colombier 		fd = fd1;
490*7dd7cddfSDavid du Colombier 		if(dirfstat(fd, &dir) < 0){
491*7dd7cddfSDavid du Colombier 			close(fd);
492*7dd7cddfSDavid du Colombier 			return fail(c, Internal);
493*7dd7cddfSDavid du Colombier 		}
494*7dd7cddfSDavid du Colombier 	}else if(p > w && p[-1] == '/'){
495*7dd7cddfSDavid du Colombier 		close(fd);
496*7dd7cddfSDavid du Colombier 		*strrchr(c->req.uri, '/') = '\0';
497*7dd7cddfSDavid du Colombier 		return moved(c, c->req.uri);
498*7dd7cddfSDavid du Colombier 	}
499*7dd7cddfSDavid du Colombier 
500*7dd7cddfSDavid du Colombier 	if(verbose)
501*7dd7cddfSDavid du Colombier 		logit(c, "%s %s %lld", c->req.meth, c->req.uri, dir.length);
502*7dd7cddfSDavid du Colombier 
503*7dd7cddfSDavid du Colombier 	return sendfd(c, fd, &dir);
504*7dd7cddfSDavid du Colombier }
505*7dd7cddfSDavid du Colombier 
506*7dd7cddfSDavid du Colombier static Strings
507*7dd7cddfSDavid du Colombier stripmagic(char *uri)
508*7dd7cddfSDavid du Colombier {
509*7dd7cddfSDavid du Colombier 	Strings ss;
510*7dd7cddfSDavid du Colombier 	char *newuri, *prog, *s;
511*7dd7cddfSDavid du Colombier 
512*7dd7cddfSDavid du Colombier 	prog = stripprefix("/magic/", uri);
513*7dd7cddfSDavid du Colombier 	if(prog == nil){
514*7dd7cddfSDavid du Colombier 		ss.s1 = uri;
515*7dd7cddfSDavid du Colombier 		ss.s2 = nil;
516*7dd7cddfSDavid du Colombier 		return ss;
517*7dd7cddfSDavid du Colombier 	}
518*7dd7cddfSDavid du Colombier 
519*7dd7cddfSDavid du Colombier 	s = strchr(prog, '/');
520*7dd7cddfSDavid du Colombier 	if(s == nil)
521*7dd7cddfSDavid du Colombier 		newuri = "";
522*7dd7cddfSDavid du Colombier 	else{
523*7dd7cddfSDavid du Colombier 		newuri = hstrdup(s);
524*7dd7cddfSDavid du Colombier 		*s = 0;
525*7dd7cddfSDavid du Colombier 		s = strrchr(s, '/');
526*7dd7cddfSDavid du Colombier 		if(s != nil && s[1] == 0)
527*7dd7cddfSDavid du Colombier 			*s = 0;
528*7dd7cddfSDavid du Colombier 	}
529*7dd7cddfSDavid du Colombier 	ss.s1 = newuri;
530*7dd7cddfSDavid du Colombier 	ss.s2 = prog;
531*7dd7cddfSDavid du Colombier 	return ss;
532*7dd7cddfSDavid du Colombier }
533*7dd7cddfSDavid du Colombier 
534*7dd7cddfSDavid du Colombier static char*
535*7dd7cddfSDavid du Colombier stripprefix(char *pre, char *str)
536*7dd7cddfSDavid du Colombier {
537*7dd7cddfSDavid du Colombier 	while(*pre)
538*7dd7cddfSDavid du Colombier 		if(*str++ != *pre++)
539*7dd7cddfSDavid du Colombier 			return nil;
540*7dd7cddfSDavid du Colombier 	return str;
541*7dd7cddfSDavid du Colombier }
542*7dd7cddfSDavid du Colombier 
543*7dd7cddfSDavid du Colombier static Strings
544*7dd7cddfSDavid du Colombier stripsearch(char *uri)
545*7dd7cddfSDavid du Colombier {
546*7dd7cddfSDavid du Colombier 	Strings ss;
547*7dd7cddfSDavid du Colombier 	char *search;
548*7dd7cddfSDavid du Colombier 
549*7dd7cddfSDavid du Colombier 	search = strchr(uri, '?');
550*7dd7cddfSDavid du Colombier 	if(search != nil)
551*7dd7cddfSDavid du Colombier 		*search++ = 0;
552*7dd7cddfSDavid du Colombier 	ss.s1 = uri;
553*7dd7cddfSDavid du Colombier 	ss.s2 = search;
554*7dd7cddfSDavid du Colombier 	return ss;
555*7dd7cddfSDavid du Colombier }
556*7dd7cddfSDavid du Colombier 
557*7dd7cddfSDavid du Colombier /*
558*7dd7cddfSDavid du Colombier  *  to circumscribe the accessible files we have to eliminate ..'s
559*7dd7cddfSDavid du Colombier  *  and resolve all names from the root.
560*7dd7cddfSDavid du Colombier  */
561*7dd7cddfSDavid du Colombier static char*
562*7dd7cddfSDavid du Colombier abspath(char *origpath, char *curdir)
563*7dd7cddfSDavid du Colombier {
564*7dd7cddfSDavid du Colombier 	char *p, *sp, *path, *work, *rpath;
565*7dd7cddfSDavid du Colombier 	int len, n, c;
566*7dd7cddfSDavid du Colombier 
567*7dd7cddfSDavid du Colombier 	if(curdir == nil)
568*7dd7cddfSDavid du Colombier 		curdir = "/";
569*7dd7cddfSDavid du Colombier 	if(origpath == nil)
570*7dd7cddfSDavid du Colombier 		origpath = "";
571*7dd7cddfSDavid du Colombier 	work = hstrdup(origpath);
572*7dd7cddfSDavid du Colombier 	path = work;
573*7dd7cddfSDavid du Colombier 
574*7dd7cddfSDavid du Colombier 	/*
575*7dd7cddfSDavid du Colombier 	 * remove any really special characters
576*7dd7cddfSDavid du Colombier 	 */
577*7dd7cddfSDavid du Colombier 	for(sp = "`;| "; *sp; sp++){
578*7dd7cddfSDavid du Colombier 		p = strchr(path, *sp);
579*7dd7cddfSDavid du Colombier 		if(p)
580*7dd7cddfSDavid du Colombier 			*p = 0;
581*7dd7cddfSDavid du Colombier 	}
582*7dd7cddfSDavid du Colombier 
583*7dd7cddfSDavid du Colombier 	len = strlen(curdir) + strlen(path) + 2 + UTFmax;
584*7dd7cddfSDavid du Colombier 	if(len < 10)
585*7dd7cddfSDavid du Colombier 		len = 10;
586*7dd7cddfSDavid du Colombier 	rpath = halloc(len);
587*7dd7cddfSDavid du Colombier 	if(*path == '/')
588*7dd7cddfSDavid du Colombier 		rpath[0] = 0;
589*7dd7cddfSDavid du Colombier 	else
590*7dd7cddfSDavid du Colombier 		strcpy(rpath, curdir);
591*7dd7cddfSDavid du Colombier 	n = strlen(rpath);
592*7dd7cddfSDavid du Colombier 
593*7dd7cddfSDavid du Colombier 	while(path){
594*7dd7cddfSDavid du Colombier 		p = strchr(path, '/');
595*7dd7cddfSDavid du Colombier 		if(p)
596*7dd7cddfSDavid du Colombier 			*p++ = 0;
597*7dd7cddfSDavid du Colombier 		if(strcmp(path, "..") == 0){
598*7dd7cddfSDavid du Colombier 			while(n > 1){
599*7dd7cddfSDavid du Colombier 				n--;
600*7dd7cddfSDavid du Colombier 				c = rpath[n];
601*7dd7cddfSDavid du Colombier 				rpath[n] = 0;
602*7dd7cddfSDavid du Colombier 				if(c == '/')
603*7dd7cddfSDavid du Colombier 					break;
604*7dd7cddfSDavid du Colombier 			}
605*7dd7cddfSDavid du Colombier 		} else if(strcmp(path, ".") == 0)
606*7dd7cddfSDavid du Colombier 			;
607*7dd7cddfSDavid du Colombier 		else if(n == 1)
608*7dd7cddfSDavid du Colombier 			n += snprint(rpath+n, len-n, "%s", path);
609*7dd7cddfSDavid du Colombier 		else
610*7dd7cddfSDavid du Colombier 			n += snprint(rpath+n, len-n, "/%s", path);
611*7dd7cddfSDavid du Colombier 		path = p;
612*7dd7cddfSDavid du Colombier 	}
613*7dd7cddfSDavid du Colombier 
614*7dd7cddfSDavid du Colombier 	if(strncmp(rpath, "/bin/", 5) == 0)
615*7dd7cddfSDavid du Colombier 		strcpy(rpath, "/");
616*7dd7cddfSDavid du Colombier 	return rpath;
617*7dd7cddfSDavid du Colombier }
618*7dd7cddfSDavid du Colombier 
619*7dd7cddfSDavid du Colombier static char*
620*7dd7cddfSDavid du Colombier getword(Connect *c)
621*7dd7cddfSDavid du Colombier {
622*7dd7cddfSDavid du Colombier 	char buf[MaxWord];
623*7dd7cddfSDavid du Colombier 	int ch, n;
624*7dd7cddfSDavid du Colombier 
625*7dd7cddfSDavid du Colombier 	while((ch = getc(c)) == ' ' || ch == '\t' || ch == '\r')
626*7dd7cddfSDavid du Colombier 		;
627*7dd7cddfSDavid du Colombier 	buf[0] = '\0';
628*7dd7cddfSDavid du Colombier 	if(ch == '\n')
629*7dd7cddfSDavid du Colombier 		return nil;
630*7dd7cddfSDavid du Colombier 	n = 0;
631*7dd7cddfSDavid du Colombier 	for(;;){
632*7dd7cddfSDavid du Colombier 		switch(ch){
633*7dd7cddfSDavid du Colombier 		case ' ':
634*7dd7cddfSDavid du Colombier 		case '\t':
635*7dd7cddfSDavid du Colombier 		case '\r':
636*7dd7cddfSDavid du Colombier 		case '\n':
637*7dd7cddfSDavid du Colombier 			buf[n] = '\0';
638*7dd7cddfSDavid du Colombier 			return hstrdup(buf);
639*7dd7cddfSDavid du Colombier 		}
640*7dd7cddfSDavid du Colombier 
641*7dd7cddfSDavid du Colombier 		if(n < MaxWord-1)
642*7dd7cddfSDavid du Colombier 			buf[n++] = ch;
643*7dd7cddfSDavid du Colombier 		ch = getc(c);
644*7dd7cddfSDavid du Colombier 	}
645*7dd7cddfSDavid du Colombier 	return nil;
646*7dd7cddfSDavid du Colombier }
647*7dd7cddfSDavid du Colombier 
648*7dd7cddfSDavid du Colombier static int
649*7dd7cddfSDavid du Colombier getc(Connect *c)
650*7dd7cddfSDavid du Colombier {
651*7dd7cddfSDavid du Colombier 	if(c->hpos < c->hstop)
652*7dd7cddfSDavid du Colombier 		return *c->hpos++;
653*7dd7cddfSDavid du Colombier 	return '\n';
654*7dd7cddfSDavid du Colombier }
655*7dd7cddfSDavid du Colombier 
656*7dd7cddfSDavid du Colombier static char*
657*7dd7cddfSDavid du Colombier sysdom(void)
658*7dd7cddfSDavid du Colombier {
659*7dd7cddfSDavid du Colombier 	char *dn;
660*7dd7cddfSDavid du Colombier 
661*7dd7cddfSDavid du Colombier 	dn = csquery("sys" , sysname(), "dom");
662*7dd7cddfSDavid du Colombier 	if(dn == nil)
663*7dd7cddfSDavid du Colombier 		dn = "who cares";
664*7dd7cddfSDavid du Colombier 	return dn;
665*7dd7cddfSDavid du Colombier }
666*7dd7cddfSDavid du Colombier 
667*7dd7cddfSDavid du Colombier /*
668*7dd7cddfSDavid du Colombier  *  query the connection server
669*7dd7cddfSDavid du Colombier  */
670*7dd7cddfSDavid du Colombier static char*
671*7dd7cddfSDavid du Colombier csquery(char *attr, char *val, char *rattr)
672*7dd7cddfSDavid du Colombier {
673*7dd7cddfSDavid du Colombier 	char token[64+4];
674*7dd7cddfSDavid du Colombier 	char buf[256], *p, *sp;
675*7dd7cddfSDavid du Colombier 	int fd, n;
676*7dd7cddfSDavid du Colombier 
677*7dd7cddfSDavid du Colombier 	if(val == nil || val[0] == 0)
678*7dd7cddfSDavid du Colombier 		return nil;
679*7dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", netdir);
680*7dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
681*7dd7cddfSDavid du Colombier 	if(fd < 0)
682*7dd7cddfSDavid du Colombier 		return nil;
683*7dd7cddfSDavid du Colombier 	fprint(fd, "!%s=%s", attr, val);
684*7dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
685*7dd7cddfSDavid du Colombier 	snprint(token, sizeof(token), "%s=", rattr);
686*7dd7cddfSDavid du Colombier 	for(;;){
687*7dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf)-1);
688*7dd7cddfSDavid du Colombier 		if(n <= 0)
689*7dd7cddfSDavid du Colombier 			break;
690*7dd7cddfSDavid du Colombier 		buf[n] = 0;
691*7dd7cddfSDavid du Colombier 		p = strstr(buf, token);
692*7dd7cddfSDavid du Colombier 		if(p != nil && (p == buf || *(p-1) == 0)){
693*7dd7cddfSDavid du Colombier 			close(fd);
694*7dd7cddfSDavid du Colombier 			sp = strchr(p, ' ');
695*7dd7cddfSDavid du Colombier 			if(sp)
696*7dd7cddfSDavid du Colombier 				*sp = 0;
697*7dd7cddfSDavid du Colombier 			p = strchr(p, '=');
698*7dd7cddfSDavid du Colombier 			if(p == nil)
699*7dd7cddfSDavid du Colombier 				return nil;
700*7dd7cddfSDavid du Colombier 			return estrdup(p+1);
701*7dd7cddfSDavid du Colombier 		}
702*7dd7cddfSDavid du Colombier 	}
703*7dd7cddfSDavid du Colombier 	close(fd);
704*7dd7cddfSDavid du Colombier 	return nil;
705*7dd7cddfSDavid du Colombier }
706*7dd7cddfSDavid du Colombier 
707*7dd7cddfSDavid du Colombier static void
708*7dd7cddfSDavid du Colombier getendpoint(char *dir, char *file, char **sysp, char **servp)
709*7dd7cddfSDavid du Colombier {
710*7dd7cddfSDavid du Colombier 	int fd, n;
711*7dd7cddfSDavid du Colombier 	char buf[128];
712*7dd7cddfSDavid du Colombier 	char *sys, *serv;
713*7dd7cddfSDavid du Colombier 
714*7dd7cddfSDavid du Colombier 	sys = serv = nil;
715*7dd7cddfSDavid du Colombier 
716*7dd7cddfSDavid du Colombier 	snprint(buf, sizeof buf, "%s/%s", dir, file);
717*7dd7cddfSDavid du Colombier 	fd = open(buf, OREAD);
718*7dd7cddfSDavid du Colombier 	if(fd >= 0){
719*7dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf)-1);
720*7dd7cddfSDavid du Colombier 		if(n>0){
721*7dd7cddfSDavid du Colombier 			buf[n-1] = 0;
722*7dd7cddfSDavid du Colombier 			serv = strchr(buf, '!');
723*7dd7cddfSDavid du Colombier 			if(serv){
724*7dd7cddfSDavid du Colombier 				*serv++ = 0;
725*7dd7cddfSDavid du Colombier 				serv = estrdup(serv);
726*7dd7cddfSDavid du Colombier 			}
727*7dd7cddfSDavid du Colombier 			sys = estrdup(buf);
728*7dd7cddfSDavid du Colombier 		}
729*7dd7cddfSDavid du Colombier 		close(fd);
730*7dd7cddfSDavid du Colombier 	}
731*7dd7cddfSDavid du Colombier 	if(serv == nil)
732*7dd7cddfSDavid du Colombier 		serv = estrdup("unknown");
733*7dd7cddfSDavid du Colombier 	if(sys == nil)
734*7dd7cddfSDavid du Colombier 		sys = estrdup("unknown");
735*7dd7cddfSDavid du Colombier 	*servp = serv;
736*7dd7cddfSDavid du Colombier 	*sysp = sys;
737*7dd7cddfSDavid du Colombier }
738*7dd7cddfSDavid du Colombier 
739*7dd7cddfSDavid du Colombier static Endpoints*
740*7dd7cddfSDavid du Colombier getendpoints(char *dir)
741*7dd7cddfSDavid du Colombier {
742*7dd7cddfSDavid du Colombier 	Endpoints *ep;
743*7dd7cddfSDavid du Colombier 
744*7dd7cddfSDavid du Colombier 	ep = ezalloc(sizeof(*ep));
745*7dd7cddfSDavid du Colombier 	getendpoint(dir, "local", &ep->lsys, &ep->lserv);
746*7dd7cddfSDavid du Colombier 	getendpoint(dir, "remote", &ep->rsys, &ep->rserv);
747*7dd7cddfSDavid du Colombier 	return ep;
748*7dd7cddfSDavid du Colombier }
749