#include #include #include #include #include #include #include "httpd.h" #include "httpsrv.h" static Hio *hout; static Hio houtb; static HConnect *connect; static int vermaj, gidwidth, uidwidth, lenwidth, devwidth; static Biobuf *aio, *dio; typedef struct Search Search; struct Search{ char *f[10]; int nf; char *g[10]; }; static int parsesearch(Search *s, char *p) { char *e; int i; memset(s, 0, sizeof *s); s->nf = getfields(p, s->f, nelem(s->f), 0, "&"); for(i = 0; i < s->nf; i++){ if(e = strchr(s->f[i], '=')){ *e = 0; s->g[i] = hurlunesc(connect, e + 1); } } return 0; } char* findsearch(Search *s, char *p) { int i; for(i = 0; i < s->nf; i++) if(!strcmp(s->f[i], p)) return s->g[i]? s->g[i]: ""; return ""; } static void doctype(void) { hprint(hout, "\n"); } void error(char *title, char *fmt, ...) { va_list arg; char buf[1024], *out; va_start(arg, fmt); out = vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); *out = 0; hprint(hout, "%s 404 %s\r\n", hversion, title); hprint(hout, "Date: %D\r\n", time(nil)); hprint(hout, "Server: Plan9\r\n"); hprint(hout, "Content-type: text/html\r\n"); hprint(hout, "\r\n"); doctype(); hprint(hout, "\n"); hprint(hout, "%s\n", title); hprint(hout, "\n"); hprint(hout, "

%s

\n", title); hprint(hout, "%s\n", buf); hprint(hout, "\n"); hprint(hout, "\n"); hflush(hout); writelog(connect, "Reply: 404\nReason: %s\n", title); exits(nil); } /* * Are we actually allowed to look in here? * * Rules: * 1) If neither allowed nor denied files exist, access is granted. * 2) If allowed exists and denied does not, dir *must* be in allowed * for access to be granted, otherwise, access is denied. * 3) If denied exists and allowed does not, dir *must not* be in * denied for access to be granted, otherwise, access is enied. * 4) If both exist, okay if either (a) file is not in denied, or * (b) in denied and in allowed. Otherwise, access is denied. */ static Reprog * getre(Biobuf *buf) { Reprog *re; char *p, *t; char *bbuf; int n; if (buf == nil) return(nil); for ( ; ; free(p)) { p = Brdstr(buf, '\n', 0); if (p == nil) return(nil); t = strchr(p, '#'); if (t != nil) *t = '\0'; t = p + strlen(p); while (--t > p && isspace(*t)) *t = '\0'; n = strlen(p); if (n == 0) continue; /* root the regular expresssion */ bbuf = malloc(n+2); if(bbuf == nil) sysfatal("out of memory"); bbuf[0] = '^'; strcpy(bbuf+1, p); re = regcomp(bbuf); free(bbuf); if (re == nil) continue; free(p); return(re); } } static int allowed(char *dir) { Reprog *re; int okay; Resub match; if (strcmp(dir, "..") == 0 || strncmp(dir, "../", 3) == 0) return(0); if (aio == nil) return(0); if (aio != nil) Bseek(aio, 0, 0); if (dio != nil) Bseek(dio, 0, 0); /* if no deny list, assume everything is denied */ okay = (dio != nil); /* go through denials till we find a match */ while (okay && (re = getre(dio)) != nil) { memset(&match, 0, sizeof(match)); okay = (regexec(re, dir, &match, 1) != 1); free(re); } /* go through accepts till we have a match */ if (aio == nil) return(okay); while (!okay && (re = getre(aio)) != nil) { memset(&match, 0, sizeof(match)); okay = (regexec(re, dir, &match, 1) == 1); free(re); } return(okay); } /* * Comparison routine for sorting the directory. */ static int compar(Dir *a, Dir *b) { return(strcmp(a->name, b->name)); } /* * These is for formating; how wide are variable-length * fields? */ static void maxwidths(Dir *dp, long n) { long i; char scratch[64]; for (i = 0; i < n; i++) { if (snprint(scratch, sizeof scratch, "%ud", dp[i].dev) > devwidth) devwidth = strlen(scratch); if (strlen(dp[i].uid) > uidwidth) uidwidth = strlen(dp[i].uid); if (strlen(dp[i].gid) > gidwidth) gidwidth = strlen(dp[i].gid); if (snprint(scratch, sizeof scratch, "%lld", dp[i].length) > lenwidth) lenwidth = strlen(scratch); } } /* * Do an actual directory listing. * asciitime is lifted directly out of ls. */ char * asciitime(long l) { ulong clk; static char buf[32]; char *t; clk = time(nil); t = ctime(l); /* 6 months in the past or a day in the future */ if(l"); if(nf <= 1){ hprint(hout, " . "); return; } hprint(hout, "%s", r, rnam(s, f[0]), "."); for(i = 1; i < nf - 1; i++){ f[i][-1] = '/'; hprint(hout, " / "); if(sallowed(s, dir)) hprint(hout, "%s", r, rnam(s, dir), f[i]); else hprint(hout, "%s", f[i]); } f[i][-1] = '/'; hprint(hout, "/ %s", f[i]); } static void dols(char *dir, Search *s) { char *f, *p, buf[256]; int i, n, fd; Dir *d; /* expand "" to "."; ``dir+1'' access below depends on this */ cleanname(dir); snprint(buf, sizeof buf, "%s/%s", findsearch(s, "p"), dir); cleanname(buf); if(!allowed(buf)) { error("Permission denied", "

Cannot list directory %s: Access prohibited

", dir); return; } fd = open(buf, OREAD); if(fd < 0) { error("Cannot read directory", "

Cannot read directory %s: %r

", dir); return; } if (vermaj) { hokheaders(connect); hprint(hout, "Content-type: text/html\r\n"); hprint(hout, "\r\n"); } doctype(); hprint(hout, "\n"); hprint(hout, "Index of %s\n", dir); hprint(hout, "\n"); rollup(s, dir); n = dirreadall(fd, &d); close(fd); maxwidths(d, n); qsort(d, n, sizeof(Dir), (int (*)(void *, void *))compar); hprint(hout, "
\n");
	for (i = 0; i < n; i++) {
		f = smprint("%s/%s", dir, d[i].name);
		cleanname(f);
		if(d[i].mode & DMDIR) {
			p = smprint("%s", rnam(s, f));
			free(f);
			f = p;
		}else
			p = fnam(s, f);
		hprint(hout, "%M %C %*ud %-*s %-*s %*lld %s %s\n",
			d[i].mode, d[i].type,
			devwidth, d[i].dev,
			uidwidth, "none" /*d[i].uid*/,
			gidwidth, "none" /*d[i].gid*/,
			lenwidth, d[i].length,
			asciitime(d[i].mtime), p, d[i].name);
		free(f);
	}
	cleanname(f = smprint("%s/..", deprefix(s, dir)));
	if(strcmp(f, "/") != 0 && sallowed(s, f))
		hprint(hout, "\nGo to parent directory\n", rnam(s, f));
	else
		hprint(hout, "\n");
	free(f);
	hprint(hout, "
\n\n\n"); hflush(hout); free(d); } /* * Handle unpacking the request in the URI and * invoking the actual handler. */ static void dosearch(char *search) { char *dir; Search s; if(parsesearch(&s, search) == -1) goto lose; if(dir = findsearch(&s, "dir")){ dols(dir, &s); return; } /* * Otherwise, we've gotten an illegal request. * spit out a non-apologetic error. */ lose: search = hurlunesc(connect, search); error("Bad directory listing request", "

Illegal formatted directory listing request:

\n" "

%H

", search); } void main(int argc, char **argv) { fmtinstall('H', httpfmt); fmtinstall('U', hurlfmt); fmtinstall('M', dirmodefmt); aio = Bopen("/sys/lib/webls.allowed", OREAD); dio = Bopen("/sys/lib/webls.denied", OREAD); if(argc == 2){ hinit(&houtb, 1, Hwrite); hout = &houtb; dols(argv[1], nil); exits(nil); } close(2); connect = init(argc, argv); hout = &connect->hout; vermaj = connect->req.vermaj; if(hparseheaders(connect, HSTIMEOUT) < 0) exits("failed"); if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){ hunallowed(connect, "GET, HEAD"); exits("not allowed"); } if(connect->head.expectother || connect->head.expectcont){ hfail(connect, HExpectFail, nil); exits("failed"); } bind("/usr/web", "/", MREPL); if(connect->req.search != nil) dosearch(connect->req.search); else error("Bad argument", "

Need a search argument

"); hflush(hout); writelog(connect, "200 sources %ld %ld\n", hout->seek, hout->seek); exits(nil); }