xref: /plan9/sys/src/cmd/ip/httpd/netlib_history.c (revision f19e7b749ec99577072cd8e44030fe810f42c7ad)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include "httpd.h"
59a747e4fSDavid du Colombier #include "httpsrv.h"
69a747e4fSDavid du Colombier 
79a747e4fSDavid du Colombier Hio *HO;
89a747e4fSDavid du Colombier int diffb;
99a747e4fSDavid du Colombier 
109a747e4fSDavid du Colombier enum{ DAY = 24*60*60 };
119a747e4fSDavid du Colombier 
129a747e4fSDavid du Colombier void
lastbefore(ulong t,char * f,char * b)139a747e4fSDavid du Colombier lastbefore(ulong t, char *f, char *b)
149a747e4fSDavid du Colombier {
159a747e4fSDavid du Colombier 	Tm *tm;
169a747e4fSDavid du Colombier 	Dir *dir;
179a747e4fSDavid du Colombier 	int try;
189a747e4fSDavid du Colombier 	ulong t0, mtime;
199a747e4fSDavid du Colombier 
209a747e4fSDavid du Colombier 	t0 = t;
219a747e4fSDavid du Colombier 	for(try=0; try<10; try++) {
229a747e4fSDavid du Colombier 		tm = localtime(t);
239a747e4fSDavid du Colombier 		t -= DAY;
249a747e4fSDavid du Colombier 		sprint(b,"%.4d/%.2d%.2d/netlib/pub/%s",tm->year+1900,tm->mon+1,tm->mday,f);
259a747e4fSDavid du Colombier 		dir = dirstat(b);
269a747e4fSDavid du Colombier 		if(dir == nil)
279a747e4fSDavid du Colombier 			continue;
289a747e4fSDavid du Colombier 		mtime = dir->mtime;
299a747e4fSDavid du Colombier 		free(dir);
309a747e4fSDavid du Colombier 		if(mtime > t0)
319a747e4fSDavid du Colombier 			continue;
329a747e4fSDavid du Colombier 		return;
339a747e4fSDavid du Colombier 	}
349a747e4fSDavid du Colombier 	strcpy(b, "filenotfound");
359a747e4fSDavid du Colombier }
369a747e4fSDavid du Colombier 
379a747e4fSDavid du Colombier // create explicit file for diff, which otherwise would create a
389a747e4fSDavid du Colombier // mode 0600 file that it couldn't read (because running as none)
399a747e4fSDavid du Colombier void
gunzip(char * f,char * tmp)409a747e4fSDavid du Colombier gunzip(char *f, char *tmp)
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier 	int fd = open(tmp, OWRITE);
439a747e4fSDavid du Colombier 
449a747e4fSDavid du Colombier 	if(fd < 0)  // can't happen
459a747e4fSDavid du Colombier 		return;
469a747e4fSDavid du Colombier 	switch(fork()){
479a747e4fSDavid du Colombier 	case 0:
489a747e4fSDavid du Colombier 		dup(fd, 1);
499a747e4fSDavid du Colombier 		close(fd);
509a747e4fSDavid du Colombier 		close(0);
51*f19e7b74SDavid du Colombier 		execl("/bin/gunzip", "gunzip", "-c", f, nil);
529a747e4fSDavid du Colombier 		hprint(HO, "can't exec gunzip: %r\n");
539a747e4fSDavid du Colombier 		break;
549a747e4fSDavid du Colombier 	case -1:
559a747e4fSDavid du Colombier 		hprint(HO, "fork failed: %r\n");
569a747e4fSDavid du Colombier 	default:
579a747e4fSDavid du Colombier 		while(waitpid() != -1)
589a747e4fSDavid du Colombier 			;
599a747e4fSDavid du Colombier 		break;
609a747e4fSDavid du Colombier 	}
619a747e4fSDavid du Colombier 	close(fd);
629a747e4fSDavid du Colombier }
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier void
netlibhistory(char * file)659a747e4fSDavid du Colombier netlibhistory(char *file)
669a747e4fSDavid du Colombier {
679a747e4fSDavid du Colombier 	char buf[500], pair[2][500], tmpf[2][30], *f;
689a747e4fSDavid du Colombier 	int toggle = 0, started = 0, limit;
699a747e4fSDavid du Colombier 	Dir *dir;
709a747e4fSDavid du Colombier 	ulong otime, dt;
719a747e4fSDavid du Colombier 	int i, fd, tmpcnt;
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier 	if(strncmp(file, "../", 3) == 0 || strstr(file, "/../") ||
749a747e4fSDavid du Colombier 		strlen(file) >= sizeof(buf) - strlen("1997/0204/netlib/pub/0"))
759a747e4fSDavid du Colombier 		return;
769a747e4fSDavid du Colombier 	limit = 50;
779a747e4fSDavid du Colombier 	if(diffb){
789a747e4fSDavid du Colombier 		limit = 10;
799a747e4fSDavid du Colombier 		// create two tmp files for gunzip
809a747e4fSDavid du Colombier 		for(i = 0, tmpcnt = 0; i < 2 && tmpcnt < 20; tmpcnt++){
819a747e4fSDavid du Colombier 			snprint(tmpf[i], sizeof(tmpf[0]), "/tmp/d%x", tmpcnt);
829a747e4fSDavid du Colombier 			if(access(buf, AEXIST) == 0)
839a747e4fSDavid du Colombier 				continue;
849a747e4fSDavid du Colombier 			fd = create(tmpf[i], OWRITE, 0666);
859a747e4fSDavid du Colombier 			if(fd < 0)
869a747e4fSDavid du Colombier 				goto done;
879a747e4fSDavid du Colombier 			close(fd);
889a747e4fSDavid du Colombier 			i++;
899a747e4fSDavid du Colombier 		}
909a747e4fSDavid du Colombier 	}
919a747e4fSDavid du Colombier 	otime = time(0);
929a747e4fSDavid du Colombier 	hprint(HO,"<UL>\n");
939a747e4fSDavid du Colombier 	while(limit--){
949a747e4fSDavid du Colombier 		lastbefore(otime, file, buf);
959a747e4fSDavid du Colombier 		dir = dirstat(buf);
969a747e4fSDavid du Colombier 		if(dir == nil)
979a747e4fSDavid du Colombier 			goto done;
989a747e4fSDavid du Colombier 		dt = DAY/2;
999a747e4fSDavid du Colombier 		while(otime <= dir->mtime){
1009a747e4fSDavid du Colombier 			lastbefore(otime-dt, file, buf);
1019a747e4fSDavid du Colombier 			free(dir);
1029a747e4fSDavid du Colombier 			dir = dirstat(buf);
1039a747e4fSDavid du Colombier 			if(dir == nil)
1049a747e4fSDavid du Colombier 				goto done;
1059a747e4fSDavid du Colombier 			dt += DAY/2;
1069a747e4fSDavid du Colombier 		}
1079a747e4fSDavid du Colombier 		f = pair[toggle];
1089a747e4fSDavid du Colombier 		strcpy(f, buf);
1099a747e4fSDavid du Colombier 		if(diffb && strcmp(f+strlen(f)-3, ".gz") == 0){
1109a747e4fSDavid du Colombier 			gunzip(f, tmpf[toggle]);
1119a747e4fSDavid du Colombier 			strcpy(f, tmpf[toggle]);
1129a747e4fSDavid du Colombier 		}
1139a747e4fSDavid du Colombier 		if(diffb && started){
1149a747e4fSDavid du Colombier 			hprint(HO, "<PRE>\n");
1159a747e4fSDavid du Colombier 			hflush(HO);
1169a747e4fSDavid du Colombier 			switch(fork()){
1179a747e4fSDavid du Colombier 			case 0:
1189a747e4fSDavid du Colombier 				execl("/bin/diff", "diff", "-nb",
119*f19e7b74SDavid du Colombier 					pair[1-toggle], pair[toggle], nil);
1209a747e4fSDavid du Colombier 				hprint(HO, "can't exec diff: %r\n");
1219a747e4fSDavid du Colombier 				break;
1229a747e4fSDavid du Colombier 			case -1:
1239a747e4fSDavid du Colombier 				hprint(HO, "fork failed: %r\n");
1249a747e4fSDavid du Colombier 				break;
1259a747e4fSDavid du Colombier 			default:
1269a747e4fSDavid du Colombier 				while(waitpid() != -1)
1279a747e4fSDavid du Colombier 					;
1289a747e4fSDavid du Colombier 				break;
1299a747e4fSDavid du Colombier 			}
1309a747e4fSDavid du Colombier 			hprint(HO, "</PRE>\n");
1319a747e4fSDavid du Colombier 		}
1329a747e4fSDavid du Colombier 		hprint(HO,"<LI><A HREF=\"/historic/%s\">%s</A> %lld bytes\n",
1339a747e4fSDavid du Colombier 			buf, 4+asctime(gmtime(dir->mtime)), dir->length);
1349a747e4fSDavid du Colombier 		if(diffb)
1359a747e4fSDavid du Colombier 			hprint(HO," <FONT SIZE=-1>(%s)</FONT>\n", pair[toggle]);
1369a747e4fSDavid du Colombier 		toggle = 1-toggle;
1379a747e4fSDavid du Colombier 		started = 1;
1389a747e4fSDavid du Colombier 		otime = dir->mtime;
1399a747e4fSDavid du Colombier 		free(dir);
1409a747e4fSDavid du Colombier 	}
1419a747e4fSDavid du Colombier 	hprint(HO,"<LI>...\n");
1429a747e4fSDavid du Colombier done:
1439a747e4fSDavid du Colombier 	hprint(HO,"</UL>\n");
1449a747e4fSDavid du Colombier 	if(diffb){
1459a747e4fSDavid du Colombier 		remove(tmpf[0]);
1469a747e4fSDavid du Colombier 		remove(tmpf[1]);
1479a747e4fSDavid du Colombier 	}
1489a747e4fSDavid du Colombier }
1499a747e4fSDavid du Colombier 
1509a747e4fSDavid du Colombier int
send(HConnect * c)1519a747e4fSDavid du Colombier send(HConnect *c)
1529a747e4fSDavid du Colombier {
1539a747e4fSDavid du Colombier 	char *file, *s;
1549a747e4fSDavid du Colombier 	HSPairs *q;
1559a747e4fSDavid du Colombier 
1569a747e4fSDavid du Colombier 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
1579a747e4fSDavid du Colombier 		return hunallowed(c, "GET, HEAD");
1589a747e4fSDavid du Colombier 	if(c->head.expectother || c->head.expectcont)
1599a747e4fSDavid du Colombier 		return hfail(c, HExpectFail, nil);
1609a747e4fSDavid du Colombier 	if(c->req.search == nil || !*c->req.search)
1619a747e4fSDavid du Colombier 		return hfail(c, HNoData, "netlib_history");
1629a747e4fSDavid du Colombier 	s = c->req.search;
1639a747e4fSDavid du Colombier 	while((s = strchr(s, '+')) != nil)
1649a747e4fSDavid du Colombier 		*s++ = ' ';
1659a747e4fSDavid du Colombier 	file = nil;
1669a747e4fSDavid du Colombier 	for(q = hparsequery(c, hstrdup(c, c->req.search)); q; q = q->next){
1679a747e4fSDavid du Colombier 		if(strcmp(q->s, "file") == 0)
1689a747e4fSDavid du Colombier 			file = q->t;
1699a747e4fSDavid du Colombier 		else if(strcmp(q->s, "diff") == 0)
1709a747e4fSDavid du Colombier 			diffb = 1;
1719a747e4fSDavid du Colombier 	}
1729a747e4fSDavid du Colombier 	if(file == nil)
1739a747e4fSDavid du Colombier 		return hfail(c, HNoData, "netlib_history missing file field");
1749a747e4fSDavid du Colombier 	logit(c, "netlib_hist %s%s", file, diffb?" DIFF":"");
1759a747e4fSDavid du Colombier 
1769a747e4fSDavid du Colombier 	if(c->req.vermaj){
1779a747e4fSDavid du Colombier 		hokheaders(c);
1789a747e4fSDavid du Colombier 		hprint(HO, "Content-type: text/html\r\n");
1799a747e4fSDavid du Colombier 		hprint(HO, "\r\n");
1809a747e4fSDavid du Colombier 	}
1819a747e4fSDavid du Colombier 	if(strcmp(c->req.meth, "HEAD") == 0){
1829a747e4fSDavid du Colombier 		writelog(c, "Reply: 200 netlib_history 0\n");
1839a747e4fSDavid du Colombier 		hflush(HO);
1849a747e4fSDavid du Colombier 		exits(nil);
1859a747e4fSDavid du Colombier 	}
1869a747e4fSDavid du Colombier 
1879a747e4fSDavid du Colombier 	hprint(HO, "<HEAD><TITLE>%s history</TITLE></HEAD>\n<BODY>\n",file);
1889a747e4fSDavid du Colombier 	hprint(HO, "<H2>%s history</H2>\n",file);
1899a747e4fSDavid du Colombier 	hprint(HO, "<I>Netlib's copy of %s was changed\n", file);
1909a747e4fSDavid du Colombier 	hprint(HO, "on the dates shown.  <BR>Click on the date link\n");
1919a747e4fSDavid du Colombier 	hprint(HO, "to retrieve the corresponding version.</I>\n");
1929a747e4fSDavid du Colombier 	if(diffb){
1939a747e4fSDavid du Colombier 		hprint(HO, "<BR><I>Lines beginning with &lt; are for the\n");
1949a747e4fSDavid du Colombier 		hprint(HO, "newer of the two versions.</I>\n");
1959a747e4fSDavid du Colombier 	}
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier 	if(chdir("/usr/web/historic") < 0)
1989a747e4fSDavid du Colombier 		hprint(HO, "chdir failed: %r\n");
1999a747e4fSDavid du Colombier 	netlibhistory(file);
2009a747e4fSDavid du Colombier 
2019a747e4fSDavid du Colombier 	hprint(HO, "<BR><A HREF=\"http://cm.bell-labs.com/who/ehg\">Eric Grosse</A>\n");
2029a747e4fSDavid du Colombier 	hprint(HO, "</BODY></HTML>\n");
2039a747e4fSDavid du Colombier 	hflush(HO);
2049a747e4fSDavid du Colombier 	writelog(c, "Reply: 200 netlib_history %ld %ld\n", HO->seek, HO->seek);
2059a747e4fSDavid du Colombier 	return 1;
2069a747e4fSDavid du Colombier }
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier void
main(int argc,char ** argv)2099a747e4fSDavid du Colombier main(int argc, char **argv)
2109a747e4fSDavid du Colombier {
2119a747e4fSDavid du Colombier 	HConnect *c;
2129a747e4fSDavid du Colombier 
2139a747e4fSDavid du Colombier 	c = init(argc, argv);
2149a747e4fSDavid du Colombier 	HO = &c->hout;
2159a747e4fSDavid du Colombier 	if(hparseheaders(c, HSTIMEOUT) >= 0)
2169a747e4fSDavid du Colombier 		send(c);
2179a747e4fSDavid du Colombier 	exits(nil);
2189a747e4fSDavid du Colombier }
219