xref: /plan9/sys/src/cmd/webfs/client.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1*9a747e4fSDavid du Colombier #include <u.h>
2*9a747e4fSDavid du Colombier #include <libc.h>
3*9a747e4fSDavid du Colombier #include <bio.h>
4*9a747e4fSDavid du Colombier #include <ip.h>
5*9a747e4fSDavid du Colombier #include <plumb.h>
6*9a747e4fSDavid du Colombier #include <thread.h>
7*9a747e4fSDavid du Colombier #include <fcall.h>
8*9a747e4fSDavid du Colombier #include <9p.h>
9*9a747e4fSDavid du Colombier #include "dat.h"
10*9a747e4fSDavid du Colombier #include "fns.h"
11*9a747e4fSDavid du Colombier 
12*9a747e4fSDavid du Colombier int nclient;
13*9a747e4fSDavid du Colombier Client **client;
14*9a747e4fSDavid du Colombier 
15*9a747e4fSDavid du Colombier static void clientthread(void*);
16*9a747e4fSDavid du Colombier int
17*9a747e4fSDavid du Colombier newclient(int plumbed)
18*9a747e4fSDavid du Colombier {
19*9a747e4fSDavid du Colombier 	int i;
20*9a747e4fSDavid du Colombier 	Client *c;
21*9a747e4fSDavid du Colombier 
22*9a747e4fSDavid du Colombier 	for(i=0; i<nclient; i++)
23*9a747e4fSDavid du Colombier 		if(client[i]->ref==0)
24*9a747e4fSDavid du Colombier 			return i;
25*9a747e4fSDavid du Colombier 
26*9a747e4fSDavid du Colombier 	c = emalloc(sizeof(Client));
27*9a747e4fSDavid du Colombier 	c->plumbed = plumbed;
28*9a747e4fSDavid du Colombier 	c->creq = chancreate(sizeof(Req*), 8);
29*9a747e4fSDavid du Colombier 	threadcreate(clientthread, c, STACK);
30*9a747e4fSDavid du Colombier 
31*9a747e4fSDavid du Colombier 	c->io = ioproc();
32*9a747e4fSDavid du Colombier 	c->num = nclient;
33*9a747e4fSDavid du Colombier 	c->ctl = globalctl;
34*9a747e4fSDavid du Colombier 	clonectl(&c->ctl);
35*9a747e4fSDavid du Colombier 	if(nclient%16 == 0)
36*9a747e4fSDavid du Colombier 		client = erealloc(client, (nclient+16)*sizeof(client[0]));
37*9a747e4fSDavid du Colombier 	client[nclient++] = c;
38*9a747e4fSDavid du Colombier 	return nclient-1;
39*9a747e4fSDavid du Colombier }
40*9a747e4fSDavid du Colombier 
41*9a747e4fSDavid du Colombier void
42*9a747e4fSDavid du Colombier closeclient(Client *c)
43*9a747e4fSDavid du Colombier {
44*9a747e4fSDavid du Colombier 	if(--c->ref == 0){
45*9a747e4fSDavid du Colombier 		if(c->bodyopened)
46*9a747e4fSDavid du Colombier 			if(c->url && c->url->close)
47*9a747e4fSDavid du Colombier 				(*c->url->close)(c);
48*9a747e4fSDavid du Colombier 		free(c->contenttype);
49*9a747e4fSDavid du Colombier 		c->contenttype = nil;
50*9a747e4fSDavid du Colombier 		free(c->postbody);
51*9a747e4fSDavid du Colombier 		c->postbody = nil;
52*9a747e4fSDavid du Colombier 		freeurl(c->url);
53*9a747e4fSDavid du Colombier 		c->url = nil;
54*9a747e4fSDavid du Colombier 		free(c->redirect);
55*9a747e4fSDavid du Colombier 		c->redirect = nil;
56*9a747e4fSDavid du Colombier 		c->npostbody = 0;
57*9a747e4fSDavid du Colombier 		c->havepostbody = 0;
58*9a747e4fSDavid du Colombier 		c->bodyopened = 0;
59*9a747e4fSDavid du Colombier 	}
60*9a747e4fSDavid du Colombier }
61*9a747e4fSDavid du Colombier 
62*9a747e4fSDavid du Colombier void
63*9a747e4fSDavid du Colombier clonectl(Ctl *c)
64*9a747e4fSDavid du Colombier {
65*9a747e4fSDavid du Colombier 	if(c->useragent)
66*9a747e4fSDavid du Colombier 		c->useragent = estrdup(c->useragent);
67*9a747e4fSDavid du Colombier }
68*9a747e4fSDavid du Colombier 
69*9a747e4fSDavid du Colombier void
70*9a747e4fSDavid du Colombier clientbodyopen(Client *c, Req *r)
71*9a747e4fSDavid du Colombier {
72*9a747e4fSDavid du Colombier 	char e[ERRMAX], *next;
73*9a747e4fSDavid du Colombier 	int i;
74*9a747e4fSDavid du Colombier 	Url *u;
75*9a747e4fSDavid du Colombier 
76*9a747e4fSDavid du Colombier 	next = nil;
77*9a747e4fSDavid du Colombier 	for(i=0; i<=c->ctl.redirectlimit; i++){
78*9a747e4fSDavid du Colombier 		if(fsdebug)
79*9a747e4fSDavid du Colombier 			fprint(2, "try %s\n", c->url->url);
80*9a747e4fSDavid du Colombier 		if(c->url->open(c, c->url) < 0){
81*9a747e4fSDavid du Colombier 		Error:
82*9a747e4fSDavid du Colombier 			if(next)
83*9a747e4fSDavid du Colombier 				fprint(2, "next %s (but for error)\n", next);
84*9a747e4fSDavid du Colombier 			free(next);
85*9a747e4fSDavid du Colombier 			rerrstr(e, sizeof e);
86*9a747e4fSDavid du Colombier 			c->iobusy = 0;
87*9a747e4fSDavid du Colombier 			if(r != nil)
88*9a747e4fSDavid du Colombier 				r->fid->omode = -1;
89*9a747e4fSDavid du Colombier 			closeclient(c);	/* not opening */
90*9a747e4fSDavid du Colombier 			if(r != nil)
91*9a747e4fSDavid du Colombier 				respond(r, e);
92*9a747e4fSDavid du Colombier 			return;
93*9a747e4fSDavid du Colombier 		}
94*9a747e4fSDavid du Colombier 		if(!c->redirect)
95*9a747e4fSDavid du Colombier 			break;
96*9a747e4fSDavid du Colombier 		next = c->redirect;
97*9a747e4fSDavid du Colombier 		c->redirect = nil;
98*9a747e4fSDavid du Colombier 		if(i==c->ctl.redirectlimit){
99*9a747e4fSDavid du Colombier 			werrstr("redirect limit reached");
100*9a747e4fSDavid du Colombier 			goto Error;
101*9a747e4fSDavid du Colombier 		}
102*9a747e4fSDavid du Colombier 		if((u = parseurl(next, c->url)) == nil)
103*9a747e4fSDavid du Colombier 			goto Error;
104*9a747e4fSDavid du Colombier 		if(urldebug)
105*9a747e4fSDavid du Colombier 			fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme);
106*9a747e4fSDavid du Colombier 		if(u->ischeme == USunknown){
107*9a747e4fSDavid du Colombier 			werrstr("redirect with unknown URL scheme");
108*9a747e4fSDavid du Colombier 			goto Error;
109*9a747e4fSDavid du Colombier 		}
110*9a747e4fSDavid du Colombier 		if(u->ischeme == UScurrent){
111*9a747e4fSDavid du Colombier 			werrstr("redirect to URL relative to current document");
112*9a747e4fSDavid du Colombier 			goto Error;
113*9a747e4fSDavid du Colombier 		}
114*9a747e4fSDavid du Colombier 		freeurl(c->url);
115*9a747e4fSDavid du Colombier 		c->url = u;
116*9a747e4fSDavid du Colombier 	}
117*9a747e4fSDavid du Colombier 	free(next);
118*9a747e4fSDavid du Colombier 	c->iobusy = 0;
119*9a747e4fSDavid du Colombier 	if(r != nil)
120*9a747e4fSDavid du Colombier 		respond(r, nil);
121*9a747e4fSDavid du Colombier }
122*9a747e4fSDavid du Colombier 
123*9a747e4fSDavid du Colombier void
124*9a747e4fSDavid du Colombier plumburl(char *url, char *base)
125*9a747e4fSDavid du Colombier {
126*9a747e4fSDavid du Colombier 	int i;
127*9a747e4fSDavid du Colombier 	Client *c;
128*9a747e4fSDavid du Colombier 
129*9a747e4fSDavid du Colombier 	i = newclient(1);
130*9a747e4fSDavid du Colombier 	c = client[i];
131*9a747e4fSDavid du Colombier 	c->ref++;
132*9a747e4fSDavid du Colombier 	if(base != nil)
133*9a747e4fSDavid du Colombier 		c->baseurl = parseurl(base, nil);
134*9a747e4fSDavid du Colombier 	c->url = parseurl(url, c->baseurl);
135*9a747e4fSDavid du Colombier 	sendp(c->creq, nil);
136*9a747e4fSDavid du Colombier }
137*9a747e4fSDavid du Colombier 
138*9a747e4fSDavid du Colombier void
139*9a747e4fSDavid du Colombier clientbodyread(Client *c, Req *r)
140*9a747e4fSDavid du Colombier {
141*9a747e4fSDavid du Colombier 	char e[ERRMAX];
142*9a747e4fSDavid du Colombier 
143*9a747e4fSDavid du Colombier 	if(c->url->read(c, r) < 0){
144*9a747e4fSDavid du Colombier 		rerrstr(e, sizeof e);
145*9a747e4fSDavid du Colombier 		c->iobusy = 0;
146*9a747e4fSDavid du Colombier 		respond(r, e);
147*9a747e4fSDavid du Colombier 	}
148*9a747e4fSDavid du Colombier 	c->iobusy = 0;
149*9a747e4fSDavid du Colombier 	respond(r, nil);
150*9a747e4fSDavid du Colombier }
151*9a747e4fSDavid du Colombier 
152*9a747e4fSDavid du Colombier static void
153*9a747e4fSDavid du Colombier clientthread(void *a)
154*9a747e4fSDavid du Colombier {
155*9a747e4fSDavid du Colombier 	Client *c;
156*9a747e4fSDavid du Colombier 	Req *r;
157*9a747e4fSDavid du Colombier 
158*9a747e4fSDavid du Colombier 	c = a;
159*9a747e4fSDavid du Colombier 	if(c->plumbed) {
160*9a747e4fSDavid du Colombier 		recvp(c->creq);
161*9a747e4fSDavid du Colombier 		clientbodyopen(c, nil);
162*9a747e4fSDavid du Colombier 		replumb(c);
163*9a747e4fSDavid du Colombier 	}
164*9a747e4fSDavid du Colombier 	while((r = recvp(c->creq)) != nil){
165*9a747e4fSDavid du Colombier 		if(fsdebug)
166*9a747e4fSDavid du Colombier 			fprint(2, "clientthread %F\n", &r->ifcall);
167*9a747e4fSDavid du Colombier 		switch(r->ifcall.type){
168*9a747e4fSDavid du Colombier 		case Topen:
169*9a747e4fSDavid du Colombier 			if(c->plumbed) {
170*9a747e4fSDavid du Colombier 				c->plumbed = 0;
171*9a747e4fSDavid du Colombier 				c->ref--;			/* from plumburl() */
172*9a747e4fSDavid du Colombier 				respond(r, nil);
173*9a747e4fSDavid du Colombier 			}
174*9a747e4fSDavid du Colombier 			else
175*9a747e4fSDavid du Colombier 				clientbodyopen(c, r);
176*9a747e4fSDavid du Colombier 			break;
177*9a747e4fSDavid du Colombier 		case Tread:
178*9a747e4fSDavid du Colombier 			clientbodyread(c, r);
179*9a747e4fSDavid du Colombier 			break;
180*9a747e4fSDavid du Colombier 		case Tflush:
181*9a747e4fSDavid du Colombier 			respond(r, nil);
182*9a747e4fSDavid du Colombier 		}
183*9a747e4fSDavid du Colombier 		if(fsdebug)
184*9a747e4fSDavid du Colombier 			fprint(2, "clientthread finished req\n");
185*9a747e4fSDavid du Colombier 	}
186*9a747e4fSDavid du Colombier }
187*9a747e4fSDavid du Colombier 
188*9a747e4fSDavid du Colombier enum
189*9a747e4fSDavid du Colombier {
190*9a747e4fSDavid du Colombier 	Bool,
191*9a747e4fSDavid du Colombier 	Int,
192*9a747e4fSDavid du Colombier 	String,
193*9a747e4fSDavid du Colombier 	XUrl,
194*9a747e4fSDavid du Colombier 	Fn,
195*9a747e4fSDavid du Colombier };
196*9a747e4fSDavid du Colombier 
197*9a747e4fSDavid du Colombier typedef struct Ctab Ctab;
198*9a747e4fSDavid du Colombier struct Ctab {
199*9a747e4fSDavid du Colombier 	char *name;
200*9a747e4fSDavid du Colombier 	int type;
201*9a747e4fSDavid du Colombier 	void *offset;
202*9a747e4fSDavid du Colombier };
203*9a747e4fSDavid du Colombier 
204*9a747e4fSDavid du Colombier Ctab ctltab[] = {
205*9a747e4fSDavid du Colombier 	"acceptcookies",	Bool,		(void*)offsetof(Ctl, acceptcookies),
206*9a747e4fSDavid du Colombier 	"sendcookies",		Bool,		(void*)offsetof(Ctl, sendcookies),
207*9a747e4fSDavid du Colombier 	"redirectlimit",		Int,		(void*)offsetof(Ctl, redirectlimit),
208*9a747e4fSDavid du Colombier 	"useragent",		String,	(void*)offsetof(Ctl, useragent),
209*9a747e4fSDavid du Colombier };
210*9a747e4fSDavid du Colombier 
211*9a747e4fSDavid du Colombier Ctab globaltab[] = {
212*9a747e4fSDavid du Colombier 	"chatty9p",		Int,		&chatty9p,
213*9a747e4fSDavid du Colombier 	"fsdebug",		Int,		&fsdebug,
214*9a747e4fSDavid du Colombier 	"cookiedebug",		Int,		&cookiedebug,
215*9a747e4fSDavid du Colombier 	"urldebug",		Int,		&urldebug,
216*9a747e4fSDavid du Colombier 	"httpdebug",		Int,		&httpdebug,
217*9a747e4fSDavid du Colombier };
218*9a747e4fSDavid du Colombier 
219*9a747e4fSDavid du Colombier Ctab clienttab[] = {
220*9a747e4fSDavid du Colombier 	"baseurl",			XUrl,		(void*)offsetof(Client, baseurl),
221*9a747e4fSDavid du Colombier 	"url",				XUrl,		(void*)offsetof(Client, url),
222*9a747e4fSDavid du Colombier };
223*9a747e4fSDavid du Colombier 
224*9a747e4fSDavid du Colombier static Ctab*
225*9a747e4fSDavid du Colombier findcmd(char *cmd, Ctab *tab, int ntab)
226*9a747e4fSDavid du Colombier {
227*9a747e4fSDavid du Colombier 	int i;
228*9a747e4fSDavid du Colombier 
229*9a747e4fSDavid du Colombier 	for(i=0; i<ntab; i++)
230*9a747e4fSDavid du Colombier 		if(strcmp(tab[i].name, cmd) == 0)
231*9a747e4fSDavid du Colombier 			return &tab[i];
232*9a747e4fSDavid du Colombier 	return nil;
233*9a747e4fSDavid du Colombier }
234*9a747e4fSDavid du Colombier 
235*9a747e4fSDavid du Colombier static void
236*9a747e4fSDavid du Colombier parseas(Req *r, char *arg, int type, void *a)
237*9a747e4fSDavid du Colombier {
238*9a747e4fSDavid du Colombier 	Url *u;
239*9a747e4fSDavid du Colombier 	char e[ERRMAX];
240*9a747e4fSDavid du Colombier 
241*9a747e4fSDavid du Colombier 	switch(type){
242*9a747e4fSDavid du Colombier 	case Bool:
243*9a747e4fSDavid du Colombier 		if(strcmp(arg, "on")==0 || strcmp(arg, "1")==0)
244*9a747e4fSDavid du Colombier 			*(int*)a = 1;
245*9a747e4fSDavid du Colombier 		else
246*9a747e4fSDavid du Colombier 			*(int*)a = 0;
247*9a747e4fSDavid du Colombier 		break;
248*9a747e4fSDavid du Colombier 	case String:
249*9a747e4fSDavid du Colombier 		free(*(char**)a);
250*9a747e4fSDavid du Colombier 		*(char**)a = estrdup(arg);
251*9a747e4fSDavid du Colombier 		break;
252*9a747e4fSDavid du Colombier 	case XUrl:
253*9a747e4fSDavid du Colombier 		u = parseurl(arg, nil);
254*9a747e4fSDavid du Colombier 		if(u == nil){
255*9a747e4fSDavid du Colombier 			snprint(e, sizeof e, "parseurl: %r");
256*9a747e4fSDavid du Colombier 			respond(r, e);
257*9a747e4fSDavid du Colombier 			return;
258*9a747e4fSDavid du Colombier 		}
259*9a747e4fSDavid du Colombier 		freeurl(*(Url**)a);
260*9a747e4fSDavid du Colombier 		*(Url**)a = u;
261*9a747e4fSDavid du Colombier 		break;
262*9a747e4fSDavid du Colombier 	case Int:
263*9a747e4fSDavid du Colombier 		if(strcmp(arg, "on")==0)
264*9a747e4fSDavid du Colombier 			*(int*)a = 1;
265*9a747e4fSDavid du Colombier 		else
266*9a747e4fSDavid du Colombier 			*(int*)a = atoi(arg);
267*9a747e4fSDavid du Colombier 		break;
268*9a747e4fSDavid du Colombier 	}
269*9a747e4fSDavid du Colombier 	respond(r, nil);
270*9a747e4fSDavid du Colombier }
271*9a747e4fSDavid du Colombier 
272*9a747e4fSDavid du Colombier int
273*9a747e4fSDavid du Colombier ctlwrite(Req *r, Ctl *ctl, char *cmd, char *arg)
274*9a747e4fSDavid du Colombier {
275*9a747e4fSDavid du Colombier 	void *a;
276*9a747e4fSDavid du Colombier 	Ctab *t;
277*9a747e4fSDavid du Colombier 
278*9a747e4fSDavid du Colombier 	if((t = findcmd(cmd, ctltab, nelem(ctltab))) == nil)
279*9a747e4fSDavid du Colombier 		return 0;
280*9a747e4fSDavid du Colombier 	a = (void*)((ulong)ctl+(int)t->offset);
281*9a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
282*9a747e4fSDavid du Colombier 	return 1;
283*9a747e4fSDavid du Colombier }
284*9a747e4fSDavid du Colombier 
285*9a747e4fSDavid du Colombier int
286*9a747e4fSDavid du Colombier clientctlwrite(Req *r, Client *c, char *cmd, char *arg)
287*9a747e4fSDavid du Colombier {
288*9a747e4fSDavid du Colombier 	void *a;
289*9a747e4fSDavid du Colombier 	Ctab *t;
290*9a747e4fSDavid du Colombier 
291*9a747e4fSDavid du Colombier 	if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil)
292*9a747e4fSDavid du Colombier 		return 0;
293*9a747e4fSDavid du Colombier 	a = (void*)((ulong)c+(int)t->offset);
294*9a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
295*9a747e4fSDavid du Colombier 	return 1;
296*9a747e4fSDavid du Colombier }
297*9a747e4fSDavid du Colombier 
298*9a747e4fSDavid du Colombier int
299*9a747e4fSDavid du Colombier globalctlwrite(Req *r, char *cmd, char *arg)
300*9a747e4fSDavid du Colombier {
301*9a747e4fSDavid du Colombier 	void *a;
302*9a747e4fSDavid du Colombier 	Ctab *t;
303*9a747e4fSDavid du Colombier 
304*9a747e4fSDavid du Colombier 	if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil)
305*9a747e4fSDavid du Colombier 		return 0;
306*9a747e4fSDavid du Colombier 	a = t->offset;
307*9a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
308*9a747e4fSDavid du Colombier 	return 1;
309*9a747e4fSDavid du Colombier }
310*9a747e4fSDavid du Colombier 
311*9a747e4fSDavid du Colombier static void
312*9a747e4fSDavid du Colombier ctlfmt(Ctl *c, char *s)
313*9a747e4fSDavid du Colombier {
314*9a747e4fSDavid du Colombier 	int i;
315*9a747e4fSDavid du Colombier 	void *a;
316*9a747e4fSDavid du Colombier 	char *t;
317*9a747e4fSDavid du Colombier 
318*9a747e4fSDavid du Colombier 	for(i=0; i<nelem(ctltab); i++){
319*9a747e4fSDavid du Colombier 		a = (void*)((ulong)c+(int)ctltab[i].offset);
320*9a747e4fSDavid du Colombier 		switch(ctltab[i].type){
321*9a747e4fSDavid du Colombier 		case Bool:
322*9a747e4fSDavid du Colombier 			s += sprint(s, "%s %s\n", ctltab[i].name, *(int*)a ? "on" : "off");
323*9a747e4fSDavid du Colombier 			break;
324*9a747e4fSDavid du Colombier 		case Int:
325*9a747e4fSDavid du Colombier 			s += sprint(s, "%s %d\n", ctltab[i].name, *(int*)a);
326*9a747e4fSDavid du Colombier 			break;
327*9a747e4fSDavid du Colombier 		case String:
328*9a747e4fSDavid du Colombier 			t = *(char**)a;
329*9a747e4fSDavid du Colombier 			if(t != nil)
330*9a747e4fSDavid du Colombier 				s += sprint(s, "%s %.*s%s\n", ctltab[i].name, utfnlen(t, 100), t, strlen(t)>100 ? "..." : "");
331*9a747e4fSDavid du Colombier 			break;
332*9a747e4fSDavid du Colombier 		}
333*9a747e4fSDavid du Colombier 	}
334*9a747e4fSDavid du Colombier }
335*9a747e4fSDavid du Colombier 
336*9a747e4fSDavid du Colombier void
337*9a747e4fSDavid du Colombier ctlread(Req *r, Client *c)
338*9a747e4fSDavid du Colombier {
339*9a747e4fSDavid du Colombier 	char buf[1024];
340*9a747e4fSDavid du Colombier 
341*9a747e4fSDavid du Colombier 	sprint(buf, "%11d \n", c->num);
342*9a747e4fSDavid du Colombier 	ctlfmt(&c->ctl, buf+strlen(buf));
343*9a747e4fSDavid du Colombier 	readstr(r, buf);
344*9a747e4fSDavid du Colombier 	respond(r, nil);
345*9a747e4fSDavid du Colombier }
346*9a747e4fSDavid du Colombier 
347*9a747e4fSDavid du Colombier void
348*9a747e4fSDavid du Colombier globalctlread(Req *r)
349*9a747e4fSDavid du Colombier {
350*9a747e4fSDavid du Colombier 	char buf[1024], *s;
351*9a747e4fSDavid du Colombier 	int i;
352*9a747e4fSDavid du Colombier 
353*9a747e4fSDavid du Colombier 	s = buf;
354*9a747e4fSDavid du Colombier 	for(i=0; i<nelem(globaltab); i++)
355*9a747e4fSDavid du Colombier 		s += sprint(s, "%s %d\n", globaltab[i].name, *(int*)globaltab[i].offset);
356*9a747e4fSDavid du Colombier 	ctlfmt(&globalctl, s);
357*9a747e4fSDavid du Colombier 	readstr(r, buf);
358*9a747e4fSDavid du Colombier 	respond(r, nil);
359*9a747e4fSDavid du Colombier }
360