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 < 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