1 /* 2 * for GET or POST to /magic/save/foo. 3 * add incoming data to foo.data. 4 * send foo.html as reply. 5 * 6 * supports foo.data with "exclusive use" mode to prevent interleaved saves. 7 * thus http://cm.bell-labs.com/magic/save/t?args should access: 8 * -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data 9 * --rw-rw-r-- M 21470 ehg web 73 May 21 18:17 /usr/web/save/t.html 10 */ 11 #include <u.h> 12 #include <libc.h> 13 #include <bio.h> 14 #include "httpd.h" 15 #include "httpsrv.h" 16 17 enum 18 { 19 MaxLog = 24*1024, /* limit on length of any one log request */ 20 LockSecs = MaxLog/500, /* seconds to wait before giving up on opening the data file */ 21 }; 22 23 static int 24 dangerous(char *s) 25 { 26 if(s == nil) 27 return 1; 28 29 /* 30 * This check shouldn't be needed; 31 * filename folding is already supposed to have happened. 32 * But I'm paranoid. 33 */ 34 while(s = strchr(s,'/')){ 35 if(s[1]=='.' && s[2]=='.') 36 return 1; 37 s++; 38 } 39 return 0; 40 } 41 42 /* 43 * open a file which might be locked. 44 * if it is, spin until available 45 */ 46 int 47 openLocked(char *file, int mode) 48 { 49 char buf[ERRMAX]; 50 int tries, fd; 51 52 for(tries = 0; tries < LockSecs*2; tries++){ 53 fd = open(file, mode); 54 if(fd >= 0) 55 return fd; 56 errstr(buf, sizeof buf); 57 if(strstr(buf, "locked") == nil) 58 break; 59 sleep(500); 60 } 61 return -1; 62 } 63 64 void 65 main(int argc, char **argv) 66 { 67 HConnect *c; 68 Dir *dir; 69 Hio *hin, *hout; 70 char *s, *t, *fn; 71 int n, nfn, datafd, htmlfd; 72 73 c = init(argc, argv); 74 75 if(dangerous(c->req.uri)){ 76 hfail(c, HSyntax); 77 exits("failed"); 78 } 79 80 if(hparseheaders(c, HSTIMEOUT) < 0) 81 exits("failed"); 82 hout = &c->hout; 83 if(c->head.expectother){ 84 hfail(c, HExpectFail, nil); 85 exits("failed"); 86 } 87 if(c->head.expectcont){ 88 hprint(hout, "100 Continue\r\n"); 89 hprint(hout, "\r\n"); 90 hflush(hout); 91 } 92 93 s = nil; 94 if(strcmp(c->req.meth, "POST") == 0){ 95 hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc); 96 if(hin != nil){ 97 alarm(HSTIMEOUT); 98 s = hreadbuf(hin, hin->pos); 99 alarm(0); 100 } 101 if(s == nil){ 102 hfail(c, HBadReq, nil); 103 exits("failed"); 104 } 105 t = strchr(s, '\n'); 106 if(t != nil) 107 *t = '\0'; 108 }else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){ 109 hunallowed(c, "GET, HEAD, PUT"); 110 exits("unallowed"); 111 }else 112 s = c->req.search; 113 if(s == nil){ 114 hfail(c, HNoData, "save"); 115 exits("failed"); 116 } 117 118 if(strlen(s) > MaxLog) 119 s[MaxLog] = '\0'; 120 n = snprint(c->xferbuf, HBufSize, "at %ld %s\n", time(0), s); 121 122 123 nfn = strlen(c->req.uri) + 64; 124 fn = halloc(c, nfn); 125 126 /* 127 * open file descriptors & write log line 128 */ 129 snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri); 130 htmlfd = open(fn, OREAD); 131 if(htmlfd < 0 || (dir = dirfstat(htmlfd)) == nil){ 132 hfail(c, HNotFound, c->req.uri); 133 exits("failed"); 134 return; 135 } 136 137 snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri); 138 datafd = openLocked(fn, OWRITE); 139 if(datafd < 0){ 140 errstr(c->xferbuf, sizeof c->xferbuf); 141 if(strstr(c->xferbuf, "locked") != nil) 142 hfail(c, HTempFail, c->req.uri); 143 else 144 hfail(c, HNotFound, c->req.uri); 145 exits("failed"); 146 } 147 seek(datafd, 0, 2); 148 write(datafd, c->xferbuf, n); 149 close(datafd); 150 151 sendfd(c, htmlfd, dir, hmkcontent(c, "text", "html", nil), nil); 152 153 exits(nil); 154 } 155