Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 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
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
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
67
clonectl(Ctl *c)
68
{
69
	if(c->useragent)
70
		c->useragent = estrdup(c->useragent);
71
}
72
 
73
void
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
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
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
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*
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
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
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
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
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
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
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
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
}