1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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);
}
|