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