Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ctype.h>
4
#include <bio.h>
5
#include <regexp.h>
6
#include <fcall.h>
7
#include "httpd.h"
8
#include "httpsrv.h"
9
 
10
static	Hio		*hout;
11
static	Hio		houtb;
12
static	HConnect	*connect;
13
static	int		vermaj, gidwidth, uidwidth, lenwidth, devwidth;
14
static	Biobuf		*aio, *dio;
15
 
16
static void
17
doctype(void)
18
{
19
	hprint(hout, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n");
20
	hprint(hout, "    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
21
}
22
 
23
void
24
error(char *title, char *fmt, ...)
25
{
26
	va_list arg;
27
	char buf[1024], *out;
28
 
29
	va_start(arg, fmt);
30
	out = vseprint(buf, buf+sizeof(buf), fmt, arg);
31
	va_end(arg);
32
	*out = 0;
33
 
34
	hprint(hout, "%s 404 %s\r\n", hversion, title);
35
	hprint(hout, "Date: %D\r\n", time(nil));
36
	hprint(hout, "Server: Plan9\r\n");
37
	hprint(hout, "Content-type: text/html\r\n");
38
	hprint(hout, "\r\n");
39
	doctype();
40
	hprint(hout, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
41
	hprint(hout, "<head><title>%s</title></head>\n", title);
42
	hprint(hout, "<body>\n");
43
	hprint(hout, "<h1>%s</h1>\n", title);
44
	hprint(hout, "%s\n", buf);
45
	hprint(hout, "</body>\n");
46
	hprint(hout, "</html>\n");
47
	hflush(hout);
48
	writelog(connect, "Reply: 404\nReason: %s\n", title);
49
	exits(nil);
50
}
51
 
52
/*
53
 * Are we actually allowed to look in here?
54
 *
55
 * Rules:
56
 *	1) If neither allowed nor denied files exist, access is granted.
57
 *	2) If allowed exists and denied does not, dir *must* be in allowed
58
 *	   for access to be granted, otherwise, access is denied.
59
 *	3) If denied exists and allowed does not, dir *must not* be in
60
 *	   denied for access to be granted, otherwise, access is enied.
61
 *	4) If both exist, okay if either (a) file is not in denied, or
62
 *	   (b) in denied and in allowed.  Otherwise, access is denied.
63
 */
64
static Reprog *
65
getre(Biobuf *buf)
66
{
67
	Reprog	*re;
68
	char	*p, *t;
69
	char	*bbuf;
70
	int	n;
71
 
72
	if (buf == nil)
73
		return(nil);
74
	for ( ; ; free(p)) {
75
		p = Brdstr(buf, '\n', 0);
76
		if (p == nil)
77
			return(nil);
78
		t = strchr(p, '#');
79
		if (t != nil)
80
			*t = '\0';
81
		t = p + strlen(p);
82
		while (--t > p && isspace(*t))
83
			*t = '\0';
84
		n = strlen(p);
85
		if (n == 0)
86
			continue;
87
 
88
		/* root the regular expresssion */
89
		bbuf = malloc(n+2);
90
		if(bbuf == nil)
91
			sysfatal("out of memory");
92
		bbuf[0] = '^';
93
		strcpy(bbuf+1, p);
94
		re = regcomp(bbuf);
95
		free(bbuf);
96
 
97
		if (re == nil)
98
			continue;
99
		free(p);
100
		return(re);
101
	}
102
}
103
 
104
static int
105
allowed(char *dir)
106
{
107
	Reprog	*re;
108
	int	okay;
109
	Resub	match;
110
 
111
	if (strcmp(dir, "..") == 0 || strncmp(dir, "../", 3) == 0)
112
		return(0);
113
	if (aio == nil)
114
		return(0);
115
 
116
	if (aio != nil)
117
		Bseek(aio, 0, 0);
118
	if (dio != nil)
119
		Bseek(dio, 0, 0);
120
 
121
	/* if no deny list, assume everything is denied */
122
	okay = (dio != nil);
123
 
124
	/* go through denials till we find a match */
125
	while (okay && (re = getre(dio)) != nil) {
126
		memset(&match, 0, sizeof(match));
127
		okay = (regexec(re, dir, &match, 1) != 1);
128
		free(re);
129
	}
130
 
131
	/* go through accepts till we have a match */
132
	if (aio == nil)
133
		return(okay);
134
	while (!okay && (re = getre(aio)) != nil) {
135
		memset(&match, 0, sizeof(match));
136
		okay = (regexec(re, dir, &match, 1) == 1);
137
		free(re);
138
	}
139
	return(okay);
140
}
141
 
142
/*
143
 * Comparison routine for sorting the directory.
144
 */
145
static int
146
compar(Dir *a, Dir *b)
147
{
148
	return(strcmp(a->name, b->name));
149
}
150
 
151
/*
152
 * These is for formating; how wide are variable-length
153
 * fields?
154
 */
155
static void
156
maxwidths(Dir *dp, long n)
157
{
158
	long	i;
159
	char	scratch[64];
160
 
161
	for (i = 0; i < n; i++) {
162
		if (snprint(scratch, sizeof scratch, "%ud", dp[i].dev) > devwidth)
163
			devwidth = strlen(scratch);
164
		if (strlen(dp[i].uid) > uidwidth)
165
			uidwidth = strlen(dp[i].uid);
166
		if (strlen(dp[i].gid) > gidwidth)
167
			gidwidth = strlen(dp[i].gid);
168
		if (snprint(scratch, sizeof scratch, "%lld", dp[i].length) > lenwidth)
169
			lenwidth = strlen(scratch);
170
	}
171
}
172
 
173
/*
174
 * Do an actual directory listing.
175
 * asciitime is lifted directly out of ls.
176
 */
177
char *
178
asciitime(long l)
179
{
180
	ulong clk;
181
	static char buf[32];
182
	char *t;
183
 
184
	clk = time(nil);
185
	t = ctime(l);
186
	/* 6 months in the past or a day in the future */
187
	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
188
		memmove(buf, t+4, 7);		/* month and day */
189
		memmove(buf+7, t+23, 5);		/* year */
190
	}else
191
		memmove(buf, t+4, 12);		/* skip day of week */
192
	buf[12] = 0;
193
	return buf;
194
}
195
 
196
static void
197
dols(char *dir)
198
{
199
	Dir	*d;
200
	char	*f, *p,*nm;
201
	long	i, n;
202
	int	fd;
203
 
204
	cleanname(dir); //  expands "" to "."; ``dir+1'' access below depends on that
205
	if (!allowed(dir)) {
206
		error("Permission denied", "<p>Cannot list directory %s: Access prohibited</p>", dir);
207
		return;
208
	}
209
	fd = open(dir, OREAD);
210
	if (fd < 0) {
211
		error("Cannot read directory", "<p>Cannot read directory %s: %r</p>", dir);
212
		return;
213
	}
214
	if (vermaj) {
215
		hokheaders(connect);
216
		hprint(hout, "Content-type: text/html\r\n");
217
		hprint(hout, "\r\n");
218
	}
219
	doctype();
220
	hprint(hout, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
221
	hprint(hout, "<head><title>Index of %s</title></head>\n", dir);
222
	hprint(hout, "<body>\n");
223
	hprint(hout, "<h1>Index of ");
224
	nm = dir;
225
	while((p = strchr(nm, '/')) != nil){
226
		*p = '\0';
227
		f = (*dir == '\0') ? "/" : dir;
228
		if (!(*dir == '\0' && *(dir+1) == '\0') && allowed(f))
229
			hprint(hout, "<a href=\"/magic/webls?dir=%H\">%s/</a>", f, nm);
230
		else
231
			hprint(hout, "%s/", nm);
232
		*p = '/';
233
		nm = p+1;
234
	}
235
	hprint(hout, "%s</h1>\n", nm);
236
	n = dirreadall(fd, &d);
237
	close(fd);
238
	maxwidths(d, n);
239
	qsort(d, n, sizeof(Dir), (int (*)(void *, void *))compar);
240
	hprint(hout, "<pre>\n");
241
	for (i = 0; i < n; i++) {
242
		f = smprint("%s/%s", dir, d[i].name);
243
		cleanname(f);
244
		if (d[i].mode & DMDIR) {
245
			p = smprint("/magic/webls?dir=%H", f);
246
			free(f);
247
			f = p;
248
		}
249
		hprint(hout, "%M %C %*ud %-*s %-*s %*lld %s <a href=\"%s\">%s</a>\n",
250
		    d[i].mode, d[i].type,
251
		    devwidth, d[i].dev,
252
		    uidwidth, d[i].uid,
253
		    gidwidth, d[i].gid,
254
		    lenwidth, d[i].length,
255
		    asciitime(d[i].mtime), f, d[i].name);
256
		free(f);
257
	}
258
	f = smprint("%s/..", dir);
259
	cleanname(f);
260
	if (strcmp(f, dir) != 0 && allowed(f))
261
		hprint(hout, "\nGo to <a href=\"/magic/webls?dir=%H\">parent</a> directory\n", f);
262
	else
263
		hprint(hout, "\nEnd of directory listing\n");
264
	free(f);
265
	hprint(hout, "</pre>\n</body>\n</html>\n");
266
	hflush(hout);
267
	free(d);
268
}
269
 
270
/*
271
 * Handle unpacking the request in the URI and
272
 * invoking the actual handler.
273
 */
274
static void
275
dosearch(char *search)
276
{
277
	if (strncmp(search, "dir=", 4) == 0){
278
		search = hurlunesc(connect, search+4);
279
		dols(search);
280
		return;
281
	}
282
 
283
	/*
284
	 * Otherwise, we've gotten an illegal request.
285
	 * spit out a non-apologetic error.
286
	 */
287
	search = hurlunesc(connect, search);
288
	error("Bad directory listing request",
289
	    "<p>Illegal formatted directory listing request:</p>\n"
290
	    "<p>%H</p>", search);
291
}
292
 
293
void
294
main(int argc, char **argv)
295
{
296
	fmtinstall('H', httpfmt);
297
	fmtinstall('U', hurlfmt);
298
	fmtinstall('M', dirmodefmt);
299
 
300
	aio = Bopen("/sys/lib/webls.allowed", OREAD);
301
	dio = Bopen("/sys/lib/webls.denied", OREAD);
302
 
303
	if(argc == 2){
304
		hinit(&houtb, 1, Hwrite);
305
		hout = &houtb;
306
		dols(argv[1]);
307
		exits(nil);
308
	}
309
	close(2);
310
 
311
	connect = init(argc, argv);
312
	hout = &connect->hout;
313
	vermaj = connect->req.vermaj;
314
	if(hparseheaders(connect, HSTIMEOUT) < 0)
315
		exits("failed");
316
 
317
	if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
318
		hunallowed(connect, "GET, HEAD");
319
		exits("not allowed");
320
	}
321
	if(connect->head.expectother || connect->head.expectcont){
322
		hfail(connect, HExpectFail, nil);
323
		exits("failed");
324
	}
325
 
326
	bind(webroot, "/", MREPL);
327
 
328
	if(connect->req.search != nil)
329
		dosearch(connect->req.search);
330
	else
331
		error("Bad argument", "<p>Need a search argument</p>");
332
	hflush(hout);
333
	writelog(connect, "200 webls %ld %ld\n", hout->seek, hout->seek);
334
	exits(nil);
335
}