summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/httpd/save.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/httpd/save.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/httpd/save.c')
-rwxr-xr-xsys/src/cmd/ip/httpd/save.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/httpd/save.c b/sys/src/cmd/ip/httpd/save.c
new file mode 100755
index 000000000..d80323ae1
--- /dev/null
+++ b/sys/src/cmd/ip/httpd/save.c
@@ -0,0 +1,154 @@
+/*
+ * 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);
+}