/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * for GET or POST to /magic/save/foo. * add incoming data to foo.data. * send foo.html as reply. * * supports foo.data with "exclusive use" mode to prevent interleaved saves. * thus http://cm.bell-labs.com/magic/save/t?args should access: * -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data * --rw-rw-r-- M 21470 ehg web 73 May 21 18:17 /usr/web/save/t.html*/#include <u.h>#include <libc.h>#include <bio.h>#include "httpd.h"#include "httpsrv.h" enum{ MaxLog = 24*1024, /* limit on length of any one log request */ LockSecs = MaxLog/500, /* seconds to wait before giving up on opening the data file */}; static intdangerous(char *s){ if(s == nil) return 1; /* * This check shouldn't be needed; * filename folding is already supposed to have happened. * But I'm paranoid. */ while(s = strchr(s,'/')){ if(s[1]=='.' && s[2]=='.') return 1; s++; } return 0;} /* * open a file which might be locked. * if it is, spin until available */intopenLocked(char *file, int mode){ char buf[ERRMAX]; int tries, fd; for(tries = 0; tries < LockSecs*2; tries++){ fd = open(file, mode); if(fd >= 0) return fd; errstr(buf, sizeof buf); if(strstr(buf, "locked") == nil) break; sleep(500); } return -1;} voidmain(int argc, char **argv){ HConnect *c; Dir *dir; Hio *hin, *hout; char *s, *t, *fn; int n, nfn, datafd, htmlfd; c = init(argc, argv); if(dangerous(c->req.uri)){ hfail(c, HSyntax); exits("failed"); } if(hparseheaders(c, HSTIMEOUT) < 0) exits("failed"); hout = &c->hout; if(c->head.expectother){ hfail(c, HExpectFail, nil); exits("failed"); } if(c->head.expectcont){ hprint(hout, "100 Continue\r\n"); hprint(hout, "\r\n"); hflush(hout); } s = nil; if(strcmp(c->req.meth, "POST") == 0){ hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc); if(hin != nil){ alarm(HSTIMEOUT); s = hreadbuf(hin, hin->pos); alarm(0); } if(s == nil){ hfail(c, HBadReq, nil); exits("failed"); } t = strchr(s, '\n'); if(t != nil) *t = '\0'; }else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){ hunallowed(c, "GET, HEAD, PUT"); exits("unallowed"); }else s = c->req.search; if(s == nil){ hfail(c, HNoData, "save"); exits("failed"); } if(strlen(s) > MaxLog) s[MaxLog] = '\0'; n = snprint(c->xferbuf, HBufSize, "at %ld %s\n", time(0), s); nfn = strlen(c->req.uri) + 64; fn = halloc(c, nfn); /* * open file descriptors & write log line */ snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri); htmlfd = open(fn, OREAD); if(htmlfd < 0 || (dir = dirfstat(htmlfd)) == nil){ hfail(c, HNotFound, c->req.uri); exits("failed"); return; } snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri); datafd = openLocked(fn, OWRITE); if(datafd < 0){ errstr(c->xferbuf, sizeof c->xferbuf); if(strstr(c->xferbuf, "locked") != nil) hfail(c, HTempFail, c->req.uri); else hfail(c, HNotFound, c->req.uri); exits("failed"); } seek(datafd, 0, 2); write(datafd, c->xferbuf, n); close(datafd); sendfd(c, htmlfd, dir, hmkcontent(c, "text", "html", nil), nil); exits(nil);}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* for GET or POST to /magic/save/foo.
* add incoming data to foo.data.
* send foo.html as reply.
*
* supports foo.data with "exclusive use" mode to prevent interleaved saves.
* thus http://cm.bell-labs.com/magic/save/t?args should access:
* -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data
* --rw-rw-r-- M 21470 ehg web 73 May 21 18:17 /usr/web/save/t.html
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "httpd.h"
#include "httpsrv.h"
enum
{
MaxLog = 24*1024, /* limit on length of any one log request */
LockSecs = MaxLog/500, /* seconds to wait before giving up on opening the data file */
};
static int
dangerous(char *s)
{
if(s == nil)
return 1;
/*
* This check shouldn't be needed;
* filename folding is already supposed to have happened.
* But I'm paranoid.
*/
while(s = strchr(s,'/')){
if(s[1]=='.' && s[2]=='.')
return 1;
s++;
}
return 0;
}
/*
* open a file which might be locked.
* if it is, spin until available
*/
int
openLocked(char *file, int mode)
{
char buf[ERRMAX];
int tries, fd;
for(tries = 0; tries < LockSecs*2; tries++){
fd = open(file, mode);
if(fd >= 0)
return fd;
errstr(buf, sizeof buf);
if(strstr(buf, "locked") == nil)
break;
sleep(500);
}
return -1;
}
void
main(int argc, char **argv)
{
HConnect *c;
Dir *dir;
Hio *hin, *hout;
char *s, *t, *fn;
int n, nfn, datafd, htmlfd;
c = init(argc, argv);
if(dangerous(c->req.uri)){
hfail(c, HSyntax);
exits("failed");
}
if(hparseheaders(c, HSTIMEOUT) < 0)
exits("failed");
hout = &c->hout;
if(c->head.expectother){
hfail(c, HExpectFail, nil);
exits("failed");
}
if(c->head.expectcont){
hprint(hout, "100 Continue\r\n");
hprint(hout, "\r\n");
hflush(hout);
}
s = nil;
if(strcmp(c->req.meth, "POST") == 0){
hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc);
if(hin != nil){
alarm(HSTIMEOUT);
s = hreadbuf(hin, hin->pos);
alarm(0);
}
if(s == nil){
hfail(c, HBadReq, nil);
exits("failed");
}
t = strchr(s, '\n');
if(t != nil)
*t = '\0';
}else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){
hunallowed(c, "GET, HEAD, PUT");
exits("unallowed");
}else
s = c->req.search;
if(s == nil){
hfail(c, HNoData, "save");
exits("failed");
}
if(strlen(s) > MaxLog)
s[MaxLog] = '\0';
n = snprint(c->xferbuf, HBufSize, "at %ld %s\n", time(0), s);
nfn = strlen(c->req.uri) + 64;
fn = halloc(c, nfn);
/*
* open file descriptors & write log line
*/
snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri);
htmlfd = open(fn, OREAD);
if(htmlfd < 0 || (dir = dirfstat(htmlfd)) == nil){
hfail(c, HNotFound, c->req.uri);
exits("failed");
return;
}
snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri);
datafd = openLocked(fn, OWRITE);
if(datafd < 0){
errstr(c->xferbuf, sizeof c->xferbuf);
if(strstr(c->xferbuf, "locked") != nil)
hfail(c, HTempFail, c->req.uri);
else
hfail(c, HNotFound, c->req.uri);
exits("failed");
}
seek(datafd, 0, 2);
write(datafd, c->xferbuf, n);
close(datafd);
sendfd(c, htmlfd, dir, hmkcontent(c, "text", "html", nil), nil);
exits(nil);
}