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 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 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, 0); 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 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], 0); 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 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 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