xref: /plan9/sys/src/cmd/ip/httpd/save.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * for GET or POST to /magic/save/foo.
37dd7cddfSDavid du Colombier  * add incoming data to foo.data.
47dd7cddfSDavid du Colombier  * send foo.html as reply.
57dd7cddfSDavid du Colombier  *
67dd7cddfSDavid du Colombier  * supports foo.data with "exclusive use" mode to prevent interleaved saves.
77dd7cddfSDavid du Colombier  * thus http://cm.bell-labs.com/magic/save/t?args should access:
87dd7cddfSDavid du Colombier  * -lrw-rw--w- M 21470 ehg web 1533 May 21 18:19 /usr/web/save/t.data
97dd7cddfSDavid du Colombier  * --rw-rw-r-- M 21470 ehg web   73 May 21 18:17 /usr/web/save/t.html
107dd7cddfSDavid du Colombier */
117dd7cddfSDavid du Colombier #include <u.h>
127dd7cddfSDavid du Colombier #include <libc.h>
137dd7cddfSDavid du Colombier #include <bio.h>
147dd7cddfSDavid du Colombier #include "httpd.h"
1580ee5cbfSDavid du Colombier #include "httpsrv.h"
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier enum
187dd7cddfSDavid du Colombier {
197dd7cddfSDavid du Colombier 	MaxLog		= 24*1024,		/* limit on length of any one log request */
207dd7cddfSDavid du Colombier 	LockSecs	= MaxLog/500,		/* seconds to wait before giving up on opening the data file */
217dd7cddfSDavid du Colombier };
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier static int
dangerous(char * s)247dd7cddfSDavid du Colombier dangerous(char *s)
257dd7cddfSDavid du Colombier {
267dd7cddfSDavid du Colombier 	if(s == nil)
277dd7cddfSDavid du Colombier 		return 1;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	/*
307dd7cddfSDavid du Colombier 	 * This check shouldn't be needed;
317dd7cddfSDavid du Colombier 	 * filename folding is already supposed to have happened.
327dd7cddfSDavid du Colombier 	 * But I'm paranoid.
337dd7cddfSDavid du Colombier 	 */
347dd7cddfSDavid du Colombier 	while(s = strchr(s,'/')){
357dd7cddfSDavid du Colombier 		if(s[1]=='.' && s[2]=='.')
367dd7cddfSDavid du Colombier 			return 1;
377dd7cddfSDavid du Colombier 		s++;
387dd7cddfSDavid du Colombier 	}
397dd7cddfSDavid du Colombier 	return 0;
407dd7cddfSDavid du Colombier }
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier /*
437dd7cddfSDavid du Colombier  * open a file which might be locked.
447dd7cddfSDavid du Colombier  * if it is, spin until available
457dd7cddfSDavid du Colombier  */
467dd7cddfSDavid du Colombier int
openLocked(char * file,int mode)477dd7cddfSDavid du Colombier openLocked(char *file, int mode)
487dd7cddfSDavid du Colombier {
49*9a747e4fSDavid du Colombier 	char buf[ERRMAX];
507dd7cddfSDavid du Colombier 	int tries, fd;
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier 	for(tries = 0; tries < LockSecs*2; tries++){
537dd7cddfSDavid du Colombier 		fd = open(file, mode);
547dd7cddfSDavid du Colombier 		if(fd >= 0)
557dd7cddfSDavid du Colombier 			return fd;
56*9a747e4fSDavid du Colombier 		errstr(buf, sizeof buf);
577dd7cddfSDavid du Colombier 		if(strstr(buf, "locked") == nil)
587dd7cddfSDavid du Colombier 			break;
597dd7cddfSDavid du Colombier 		sleep(500);
607dd7cddfSDavid du Colombier 	}
617dd7cddfSDavid du Colombier 	return -1;
627dd7cddfSDavid du Colombier }
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier void
main(int argc,char ** argv)657dd7cddfSDavid du Colombier main(int argc, char **argv)
667dd7cddfSDavid du Colombier {
6780ee5cbfSDavid du Colombier 	HConnect *c;
68*9a747e4fSDavid du Colombier 	Dir *dir;
697dd7cddfSDavid du Colombier 	Hio *hin, *hout;
707dd7cddfSDavid du Colombier 	char *s, *t, *fn;
717dd7cddfSDavid du Colombier 	int n, nfn, datafd, htmlfd;
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier 	c = init(argc, argv);
747dd7cddfSDavid du Colombier 
7580ee5cbfSDavid du Colombier 	if(dangerous(c->req.uri)){
7680ee5cbfSDavid du Colombier 		hfail(c, HSyntax);
7780ee5cbfSDavid du Colombier 		exits("failed");
7880ee5cbfSDavid du Colombier 	}
797dd7cddfSDavid du Colombier 
8080ee5cbfSDavid du Colombier 	if(hparseheaders(c, HSTIMEOUT) < 0)
8180ee5cbfSDavid du Colombier 		exits("failed");
827dd7cddfSDavid du Colombier 	hout = &c->hout;
8380ee5cbfSDavid du Colombier 	if(c->head.expectother){
8480ee5cbfSDavid du Colombier 		hfail(c, HExpectFail, nil);
8580ee5cbfSDavid du Colombier 		exits("failed");
8680ee5cbfSDavid du Colombier 	}
877dd7cddfSDavid du Colombier 	if(c->head.expectcont){
887dd7cddfSDavid du Colombier 		hprint(hout, "100 Continue\r\n");
897dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
907dd7cddfSDavid du Colombier 		hflush(hout);
917dd7cddfSDavid du Colombier 	}
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	s = nil;
947dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "POST") == 0){
957dd7cddfSDavid du Colombier 		hin = hbodypush(&c->hin, c->head.contlen, c->head.transenc);
967dd7cddfSDavid du Colombier 		if(hin != nil){
9780ee5cbfSDavid du Colombier 			alarm(HSTIMEOUT);
987dd7cddfSDavid du Colombier 			s = hreadbuf(hin, hin->pos);
997dd7cddfSDavid du Colombier 			alarm(0);
1007dd7cddfSDavid du Colombier 		}
10180ee5cbfSDavid du Colombier 		if(s == nil){
10280ee5cbfSDavid du Colombier 			hfail(c, HBadReq, nil);
10380ee5cbfSDavid du Colombier 			exits("failed");
10480ee5cbfSDavid du Colombier 		}
1057dd7cddfSDavid du Colombier 		t = strchr(s, '\n');
1067dd7cddfSDavid du Colombier 		if(t != nil)
1077dd7cddfSDavid du Colombier 			*t = '\0';
10880ee5cbfSDavid du Colombier 	}else if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0){
10980ee5cbfSDavid du Colombier 		hunallowed(c, "GET, HEAD, PUT");
11080ee5cbfSDavid du Colombier 		exits("unallowed");
11180ee5cbfSDavid du Colombier 	}else
1127dd7cddfSDavid du Colombier 		s = c->req.search;
11380ee5cbfSDavid du Colombier 	if(s == nil){
11480ee5cbfSDavid du Colombier 		hfail(c, HNoData, "save");
11580ee5cbfSDavid du Colombier 		exits("failed");
11680ee5cbfSDavid du Colombier 	}
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier 	if(strlen(s) > MaxLog)
1197dd7cddfSDavid du Colombier 		s[MaxLog] = '\0';
12080ee5cbfSDavid du Colombier 	n = snprint(c->xferbuf, HBufSize, "at %ld %s\n", time(0), s);
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 
123*9a747e4fSDavid du Colombier 	nfn = strlen(c->req.uri) + 64;
12480ee5cbfSDavid du Colombier 	fn = halloc(c, nfn);
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	/*
1277dd7cddfSDavid du Colombier 	 * open file descriptors & write log line
1287dd7cddfSDavid du Colombier 	 */
1297dd7cddfSDavid du Colombier 	snprint(fn, nfn, "/usr/web/save/%s.html", c->req.uri);
1307dd7cddfSDavid du Colombier 	htmlfd = open(fn, OREAD);
131*9a747e4fSDavid du Colombier 	if(htmlfd < 0 || (dir = dirfstat(htmlfd)) == nil){
13280ee5cbfSDavid du Colombier 		hfail(c, HNotFound, c->req.uri);
13380ee5cbfSDavid du Colombier 		exits("failed");
134*9a747e4fSDavid du Colombier 		return;
13580ee5cbfSDavid du Colombier 	}
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier 	snprint(fn, nfn, "/usr/web/save/%s.data", c->req.uri);
1387dd7cddfSDavid du Colombier 	datafd = openLocked(fn, OWRITE);
1397dd7cddfSDavid du Colombier 	if(datafd < 0){
140*9a747e4fSDavid du Colombier 		errstr(c->xferbuf, sizeof c->xferbuf);
1417dd7cddfSDavid du Colombier 		if(strstr(c->xferbuf, "locked") != nil)
14280ee5cbfSDavid du Colombier 			hfail(c, HTempFail, c->req.uri);
14380ee5cbfSDavid du Colombier 		else
14480ee5cbfSDavid du Colombier 			hfail(c, HNotFound, c->req.uri);
14580ee5cbfSDavid du Colombier 		exits("failed");
1467dd7cddfSDavid du Colombier 	}
1477dd7cddfSDavid du Colombier 	seek(datafd, 0, 2);
1487dd7cddfSDavid du Colombier 	write(datafd, c->xferbuf, n);
1497dd7cddfSDavid du Colombier 	close(datafd);
1507dd7cddfSDavid du Colombier 
151*9a747e4fSDavid du Colombier 	sendfd(c, htmlfd, dir, hmkcontent(c, "text", "html", nil), nil);
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 	exits(nil);
1547dd7cddfSDavid du Colombier }
155