xref: /plan9/sys/src/cmd/ip/httpd/save.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier /*
2*7dd7cddfSDavid du Colombier  * for GET or POST to /magic/save/foo.
3*7dd7cddfSDavid du Colombier  * add incoming data to foo.data.
4*7dd7cddfSDavid du Colombier  * send foo.html as reply.
5*7dd7cddfSDavid du Colombier  *
6*7dd7cddfSDavid du Colombier  * supports foo.data with "exclusive use" mode to prevent interleaved saves.
7*7dd7cddfSDavid du Colombier  * thus http://cm.bell-labs.com/magic/save/t?args should access:
8*7dd7cddfSDavid du Colombier  * -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data
9*7dd7cddfSDavid du Colombier  * --rw-rw-r-- M 21470 ehg web   73 May 21 18:17 /usr/web/save/t.html
10*7dd7cddfSDavid du Colombier */
11*7dd7cddfSDavid du Colombier #include <u.h>
12*7dd7cddfSDavid du Colombier #include <libc.h>
13*7dd7cddfSDavid du Colombier #include <bio.h>
14*7dd7cddfSDavid du Colombier #include "httpd.h"
15*7dd7cddfSDavid du Colombier 
16*7dd7cddfSDavid du Colombier enum
17*7dd7cddfSDavid du Colombier {
18*7dd7cddfSDavid du Colombier 	MaxLog		= 24*1024,		/* limit on length of any one log request */
19*7dd7cddfSDavid du Colombier 	LockSecs	= MaxLog/500,		/* seconds to wait before giving up on opening the data file */
20*7dd7cddfSDavid du Colombier };
21*7dd7cddfSDavid du Colombier 
22*7dd7cddfSDavid du Colombier static int
23*7dd7cddfSDavid du Colombier dangerous(char *s)
24*7dd7cddfSDavid du Colombier {
25*7dd7cddfSDavid du Colombier 	if(s == nil)
26*7dd7cddfSDavid du Colombier 		return 1;
27*7dd7cddfSDavid du Colombier 
28*7dd7cddfSDavid du Colombier 	/*
29*7dd7cddfSDavid du Colombier 	 * This check shouldn't be needed;
30*7dd7cddfSDavid du Colombier 	 * filename folding is already supposed to have happened.
31*7dd7cddfSDavid du Colombier 	 * But I'm paranoid.
32*7dd7cddfSDavid du Colombier 	 */
33*7dd7cddfSDavid du Colombier 	while(s = strchr(s,'/')){
34*7dd7cddfSDavid du Colombier 		if(s[1]=='.' && s[2]=='.')
35*7dd7cddfSDavid du Colombier 			return 1;
36*7dd7cddfSDavid du Colombier 		s++;
37*7dd7cddfSDavid du Colombier 	}
38*7dd7cddfSDavid du Colombier 	return 0;
39*7dd7cddfSDavid du Colombier }
40*7dd7cddfSDavid du Colombier 
41*7dd7cddfSDavid du Colombier /*
42*7dd7cddfSDavid du Colombier  * open a file which might be locked.
43*7dd7cddfSDavid du Colombier  * if it is, spin until available
44*7dd7cddfSDavid du Colombier  */
45*7dd7cddfSDavid du Colombier int
46*7dd7cddfSDavid du Colombier openLocked(char *file, int mode)
47*7dd7cddfSDavid du Colombier {
48*7dd7cddfSDavid du Colombier 	char buf[ERRLEN];
49*7dd7cddfSDavid du Colombier 	int tries, fd;
50*7dd7cddfSDavid du Colombier 
51*7dd7cddfSDavid du Colombier 	for(tries = 0; tries < LockSecs*2; tries++){
52*7dd7cddfSDavid du Colombier 		fd = open(file, mode);
53*7dd7cddfSDavid du Colombier 		if(fd >= 0)
54*7dd7cddfSDavid du Colombier 			return fd;
55*7dd7cddfSDavid du Colombier 		errstr(buf);
56*7dd7cddfSDavid du Colombier 		if(strstr(buf, "locked") == nil)
57*7dd7cddfSDavid du Colombier 			break;
58*7dd7cddfSDavid du Colombier 		sleep(500);
59*7dd7cddfSDavid du Colombier 	}
60*7dd7cddfSDavid du Colombier 	return -1;
61*7dd7cddfSDavid du Colombier }
62*7dd7cddfSDavid du Colombier 
63*7dd7cddfSDavid du Colombier void
64*7dd7cddfSDavid du Colombier main(int argc, char **argv)
65*7dd7cddfSDavid du Colombier {
66*7dd7cddfSDavid du Colombier 	Connect *c;
67*7dd7cddfSDavid du Colombier 	Dir dir;
68*7dd7cddfSDavid du Colombier 	Hio *hin, *hout;
69*7dd7cddfSDavid du Colombier 	char *s, *t, *fn;
70*7dd7cddfSDavid du Colombier 	int n, nfn, datafd, htmlfd;
71*7dd7cddfSDavid du Colombier 
72*7dd7cddfSDavid du Colombier 	c = init(argc, argv);
73*7dd7cddfSDavid du Colombier 
74*7dd7cddfSDavid du Colombier 	if(dangerous(c->req.uri))
75*7dd7cddfSDavid du Colombier 		fail(c, Syntax);
76*7dd7cddfSDavid du Colombier 
77*7dd7cddfSDavid du Colombier 	httpheaders(c);
78*7dd7cddfSDavid du Colombier 	hout = &c->hout;
79*7dd7cddfSDavid du Colombier 	if(c->head.expectother)
80*7dd7cddfSDavid du Colombier 		fail(c, ExpectFail, nil);
81*7dd7cddfSDavid du Colombier 	if(c->head.expectcont){
82*7dd7cddfSDavid du Colombier 		hprint(hout, "100 Continue\r\n");
83*7dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
84*7dd7cddfSDavid du Colombier 		hflush(hout);
85*7dd7cddfSDavid du Colombier 	}
86*7dd7cddfSDavid du Colombier 
87*7dd7cddfSDavid du Colombier 	s = nil;
88*7dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "POST") == 0){
89*7dd7cddfSDavid du Colombier 		hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc);
90*7dd7cddfSDavid du Colombier 		if(hin != nil){
91*7dd7cddfSDavid du Colombier 			alarm(15*60*1000);
92*7dd7cddfSDavid du Colombier 			s = hreadbuf(hin, hin->pos);
93*7dd7cddfSDavid du Colombier 			alarm(0);
94*7dd7cddfSDavid du Colombier 		}
95*7dd7cddfSDavid du Colombier 		if(s == nil)
96*7dd7cddfSDavid du Colombier 			fail(c, BadReq, nil);
97*7dd7cddfSDavid du Colombier 		t = strchr(s, '\n');
98*7dd7cddfSDavid du Colombier 		if(t != nil)
99*7dd7cddfSDavid du Colombier 			*t = '\0';
100*7dd7cddfSDavid du Colombier 	}else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0)
101*7dd7cddfSDavid du Colombier 		unallowed(c, "GET, HEAD, PUT");
102*7dd7cddfSDavid du Colombier 	else
103*7dd7cddfSDavid du Colombier 		s = c->req.search;
104*7dd7cddfSDavid du Colombier 	if(s == nil)
105*7dd7cddfSDavid du Colombier 		fail(c, NoData, "save");
106*7dd7cddfSDavid du Colombier 
107*7dd7cddfSDavid du Colombier 	if(strlen(s) > MaxLog)
108*7dd7cddfSDavid du Colombier 		s[MaxLog] = '\0';
109*7dd7cddfSDavid du Colombier 	n = snprint(c->xferbuf, BufSize, "at %ld %s\n", time(0), s);
110*7dd7cddfSDavid du Colombier 
111*7dd7cddfSDavid du Colombier 
112*7dd7cddfSDavid du Colombier 	nfn = strlen(c->req.uri) + 4 * NAMELEN;
113*7dd7cddfSDavid du Colombier 	fn = halloc(nfn);
114*7dd7cddfSDavid du Colombier 
115*7dd7cddfSDavid du Colombier 	/*
116*7dd7cddfSDavid du Colombier 	 * open file descriptors & write log line
117*7dd7cddfSDavid du Colombier 	 */
118*7dd7cddfSDavid du Colombier 	snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri);
119*7dd7cddfSDavid du Colombier 	htmlfd = open(fn, OREAD);
120*7dd7cddfSDavid du Colombier 	if(htmlfd < 0 || dirfstat(htmlfd, &dir) < 0)
121*7dd7cddfSDavid du Colombier 		fail(c, NotFound, c->req.uri);
122*7dd7cddfSDavid du Colombier 
123*7dd7cddfSDavid du Colombier 	snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri);
124*7dd7cddfSDavid du Colombier 	datafd = openLocked(fn, OWRITE);
125*7dd7cddfSDavid du Colombier 	if(datafd < 0){
126*7dd7cddfSDavid du Colombier 		errstr(c->xferbuf);
127*7dd7cddfSDavid du Colombier 		if(strstr(c->xferbuf, "locked") != nil)
128*7dd7cddfSDavid du Colombier 			fail(c, TempFail, c->req.uri);
129*7dd7cddfSDavid du Colombier 		fail(c, NotFound, c->req.uri);
130*7dd7cddfSDavid du Colombier 	}
131*7dd7cddfSDavid du Colombier 	seek(datafd, 0, 2);
132*7dd7cddfSDavid du Colombier 	write(datafd, c->xferbuf, n);
133*7dd7cddfSDavid du Colombier 	close(datafd);
134*7dd7cddfSDavid du Colombier 
135*7dd7cddfSDavid du Colombier 	/*
136*7dd7cddfSDavid du Colombier 	 * send the reply
137*7dd7cddfSDavid du Colombier 	 */
138*7dd7cddfSDavid du Colombier 	if(c->req.vermaj){
139*7dd7cddfSDavid du Colombier 		okheaders(c);
140*7dd7cddfSDavid du Colombier 		hprint(hout, "Content-type: text/html\r\n");
141*7dd7cddfSDavid du Colombier 		hprint(hout, "Content-Length: %lld\r\n", dir.length);
142*7dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
143*7dd7cddfSDavid du Colombier 	}
144*7dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "HEAD") == 0)
145*7dd7cddfSDavid du Colombier 		exits(0);
146*7dd7cddfSDavid du Colombier 
147*7dd7cddfSDavid du Colombier 	hflush(hout);
148*7dd7cddfSDavid du Colombier 	while((n = read(htmlfd, c->xferbuf, BufSize)) > 0)
149*7dd7cddfSDavid du Colombier 		if(write(1, c->xferbuf, n) != n)
150*7dd7cddfSDavid du Colombier 			break;
151*7dd7cddfSDavid du Colombier 
152*7dd7cddfSDavid du Colombier 	exits(nil);
153*7dd7cddfSDavid du Colombier }
154