Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}