]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/httpd/save.c
fix fuckup
[plan9front.git] / sys / src / cmd / ip / httpd / save.c
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 }