xref: /plan9/sys/src/cmd/ip/httpd/netlib_history.c (revision f19e7b749ec99577072cd8e44030fe810f42c7ad)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "httpd.h"
5 #include "httpsrv.h"
6 
7 Hio *HO;
8 int diffb;
9 
10 enum{ DAY = 24*60*60 };
11 
12 void
lastbefore(ulong t,char * f,char * b)13 lastbefore(ulong t, char *f, char *b)
14 {
15 	Tm *tm;
16 	Dir *dir;
17 	int try;
18 	ulong t0, mtime;
19 
20 	t0 = t;
21 	for(try=0; try<10; try++) {
22 		tm = localtime(t);
23 		t -= DAY;
24 		sprint(b,"%.4d/%.2d%.2d/netlib/pub/%s",tm->year+1900,tm->mon+1,tm->mday,f);
25 		dir = dirstat(b);
26 		if(dir == nil)
27 			continue;
28 		mtime = dir->mtime;
29 		free(dir);
30 		if(mtime > t0)
31 			continue;
32 		return;
33 	}
34 	strcpy(b, "filenotfound");
35 }
36 
37 // create explicit file for diff, which otherwise would create a
38 // mode 0600 file that it couldn't read (because running as none)
39 void
gunzip(char * f,char * tmp)40 gunzip(char *f, char *tmp)
41 {
42 	int fd = open(tmp, OWRITE);
43 
44 	if(fd < 0)  // can't happen
45 		return;
46 	switch(fork()){
47 	case 0:
48 		dup(fd, 1);
49 		close(fd);
50 		close(0);
51 		execl("/bin/gunzip", "gunzip", "-c", f, nil);
52 		hprint(HO, "can't exec gunzip: %r\n");
53 		break;
54 	case -1:
55 		hprint(HO, "fork failed: %r\n");
56 	default:
57 		while(waitpid() != -1)
58 			;
59 		break;
60 	}
61 	close(fd);
62 }
63 
64 void
netlibhistory(char * file)65 netlibhistory(char *file)
66 {
67 	char buf[500], pair[2][500], tmpf[2][30], *f;
68 	int toggle = 0, started = 0, limit;
69 	Dir *dir;
70 	ulong otime, dt;
71 	int i, fd, tmpcnt;
72 
73 	if(strncmp(file, "../", 3) == 0 || strstr(file, "/../") ||
74 		strlen(file) >= sizeof(buf) - strlen("1997/0204/netlib/pub/0"))
75 		return;
76 	limit = 50;
77 	if(diffb){
78 		limit = 10;
79 		// create two tmp files for gunzip
80 		for(i = 0, tmpcnt = 0; i < 2 && tmpcnt < 20; tmpcnt++){
81 			snprint(tmpf[i], sizeof(tmpf[0]), "/tmp/d%x", tmpcnt);
82 			if(access(buf, AEXIST) == 0)
83 				continue;
84 			fd = create(tmpf[i], OWRITE, 0666);
85 			if(fd < 0)
86 				goto done;
87 			close(fd);
88 			i++;
89 		}
90 	}
91 	otime = time(0);
92 	hprint(HO,"<UL>\n");
93 	while(limit--){
94 		lastbefore(otime, file, buf);
95 		dir = dirstat(buf);
96 		if(dir == nil)
97 			goto done;
98 		dt = DAY/2;
99 		while(otime <= dir->mtime){
100 			lastbefore(otime-dt, file, buf);
101 			free(dir);
102 			dir = dirstat(buf);
103 			if(dir == nil)
104 				goto done;
105 			dt += DAY/2;
106 		}
107 		f = pair[toggle];
108 		strcpy(f, buf);
109 		if(diffb && strcmp(f+strlen(f)-3, ".gz") == 0){
110 			gunzip(f, tmpf[toggle]);
111 			strcpy(f, tmpf[toggle]);
112 		}
113 		if(diffb && started){
114 			hprint(HO, "<PRE>\n");
115 			hflush(HO);
116 			switch(fork()){
117 			case 0:
118 				execl("/bin/diff", "diff", "-nb",
119 					pair[1-toggle], pair[toggle], nil);
120 				hprint(HO, "can't exec diff: %r\n");
121 				break;
122 			case -1:
123 				hprint(HO, "fork failed: %r\n");
124 				break;
125 			default:
126 				while(waitpid() != -1)
127 					;
128 				break;
129 			}
130 			hprint(HO, "</PRE>\n");
131 		}
132 		hprint(HO,"<LI><A HREF=\"/historic/%s\">%s</A> %lld bytes\n",
133 			buf, 4+asctime(gmtime(dir->mtime)), dir->length);
134 		if(diffb)
135 			hprint(HO," <FONT SIZE=-1>(%s)</FONT>\n", pair[toggle]);
136 		toggle = 1-toggle;
137 		started = 1;
138 		otime = dir->mtime;
139 		free(dir);
140 	}
141 	hprint(HO,"<LI>...\n");
142 done:
143 	hprint(HO,"</UL>\n");
144 	if(diffb){
145 		remove(tmpf[0]);
146 		remove(tmpf[1]);
147 	}
148 }
149 
150 int
send(HConnect * c)151 send(HConnect *c)
152 {
153 	char *file, *s;
154 	HSPairs *q;
155 
156 	if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
157 		return hunallowed(c, "GET, HEAD");
158 	if(c->head.expectother || c->head.expectcont)
159 		return hfail(c, HExpectFail, nil);
160 	if(c->req.search == nil || !*c->req.search)
161 		return hfail(c, HNoData, "netlib_history");
162 	s = c->req.search;
163 	while((s = strchr(s, '+')) != nil)
164 		*s++ = ' ';
165 	file = nil;
166 	for(q = hparsequery(c, hstrdup(c, c->req.search)); q; q = q->next){
167 		if(strcmp(q->s, "file") == 0)
168 			file = q->t;
169 		else if(strcmp(q->s, "diff") == 0)
170 			diffb = 1;
171 	}
172 	if(file == nil)
173 		return hfail(c, HNoData, "netlib_history missing file field");
174 	logit(c, "netlib_hist %s%s", file, diffb?" DIFF":"");
175 
176 	if(c->req.vermaj){
177 		hokheaders(c);
178 		hprint(HO, "Content-type: text/html\r\n");
179 		hprint(HO, "\r\n");
180 	}
181 	if(strcmp(c->req.meth, "HEAD") == 0){
182 		writelog(c, "Reply: 200 netlib_history 0\n");
183 		hflush(HO);
184 		exits(nil);
185 	}
186 
187 	hprint(HO, "<HEAD><TITLE>%s history</TITLE></HEAD>\n<BODY>\n",file);
188 	hprint(HO, "<H2>%s history</H2>\n",file);
189 	hprint(HO, "<I>Netlib's copy of %s was changed\n", file);
190 	hprint(HO, "on the dates shown.  <BR>Click on the date link\n");
191 	hprint(HO, "to retrieve the corresponding version.</I>\n");
192 	if(diffb){
193 		hprint(HO, "<BR><I>Lines beginning with &lt; are for the\n");
194 		hprint(HO, "newer of the two versions.</I>\n");
195 	}
196 
197 	if(chdir("/usr/web/historic") < 0)
198 		hprint(HO, "chdir failed: %r\n");
199 	netlibhistory(file);
200 
201 	hprint(HO, "<BR><A HREF=\"http://cm.bell-labs.com/who/ehg\">Eric Grosse</A>\n");
202 	hprint(HO, "</BODY></HTML>\n");
203 	hflush(HO);
204 	writelog(c, "Reply: 200 netlib_history %ld %ld\n", HO->seek, HO->seek);
205 	return 1;
206 }
207 
208 void
main(int argc,char ** argv)209 main(int argc, char **argv)
210 {
211 	HConnect *c;
212 
213 	c = init(argc, argv);
214 	HO = &c->hout;
215 	if(hparseheaders(c, HSTIMEOUT) >= 0)
216 		send(c);
217 	exits(nil);
218 }
219