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 <libsec.h>
10
#include <auth.h>
11
#include "dat.h"
12
#include "fns.h"
13
 
14
char PostContentType[] = "application/x-www-form-urlencoded";
15
int httpdebug;
16
 
17
typedef struct HttpState HttpState;
18
struct HttpState
19
{
20
	int fd;
21
	Client *c;
22
	char *location;
23
	char *setcookie;
24
	char *netaddr;
25
	char *credentials;
26
	char autherror[ERRMAX];
27
	Ibuf	b;
28
};
29
 
30
static void
31
location(HttpState *hs, char *value)
32
{
33
	if(hs->location == nil)
34
		hs->location = estrdup(value);
35
}
36
 
37
static void
38
contenttype(HttpState *hs, char *value)
39
{
40
	if(hs->c->contenttype != nil)
41
		free(hs->c->contenttype);
42
	hs->c->contenttype = estrdup(value);
43
}
44
 
45
static void
46
setcookie(HttpState *hs, char *value)
47
{
48
	char *s, *t;
49
	Fmt f;
50
 
51
	s = hs->setcookie;
52
	fmtstrinit(&f);
53
	if(s)
54
		fmtprint(&f, "%s", s);
55
	fmtprint(&f, "set-cookie: ");
56
	fmtprint(&f, "%s", value);
57
	fmtprint(&f, "\n");
58
	t = fmtstrflush(&f);
59
	if(t){
60
		free(s);
61
		hs->setcookie = t;
62
	}
63
}
64
 
65
static char*
66
unquote(char *s, char **ps)
67
{
68
	char *p;
69
 
70
	if(*s != '"'){
71
		p = strpbrk(s, " \t\r\n");
72
		*p++ = 0;
73
		*ps = p;
74
		return s;
75
	}
76
	for(p=s+1; *p; p++){
77
		if(*p == '\"'){
78
			*p++ = 0;
79
			break;
80
		}
81
		if(*p == '\\' && *(p+1)){
82
			p++;
83
			continue;
84
		}
85
	}
86
	memmove(s, s+1, p-(s+1));
87
	s[p-(s+1)] = 0;
88
	*ps = p;
89
	return s;
90
}
91
 
92
static char*
93
servername(char *addr)
94
{
95
	char *p;
96
 
97
	if(strncmp(addr, "tcp!", 4) == 0
98
	|| strncmp(addr, "net!", 4) == 0)
99
		addr += 4;
100
	addr = estrdup(addr);
101
	p = addr+strlen(addr);
102
	if(p>addr && *(p-1) == 's')
103
		p--;
104
	if(p>addr+5 && strcmp(p-5, "!http") == 0)
105
		p[-5] = 0;
106
	return addr;
107
}
108
 
109
void
110
wwwauthenticate(HttpState *hs, char *line)
111
{
112
	char cred[64], *user, *pass, *realm, *s, *spec, *name;
113
	Fmt fmt;
114
	UserPasswd *up;
115
 
116
	spec = nil;
117
	up = nil;
118
	cred[0] = 0;
119
	hs->autherror[0] = 0;
120
	if(cistrncmp(line, "basic ", 6) != 0){
121
		werrstr("unknown auth: %s", line);
122
		goto error;
123
	}
124
	line += 6;
125
	if(cistrncmp(line, "realm=", 6) != 0){
126
		werrstr("missing realm: %s", line);
127
		goto error;
128
	}
129
	line += 6;
130
	user = hs->c->url->user;
131
	pass = hs->c->url->passwd;
132
	if(user==nil || pass==nil){
133
		realm = unquote(line, &line);
134
		fmtstrinit(&fmt);
135
		name = servername(hs->netaddr);
136
		fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm);
137
		free(name);
138
		if(hs->c->url->user)
139
			fmtprint(&fmt, " user=%q", hs->c->url->user);
140
		spec = fmtstrflush(&fmt);
141
		if(spec == nil)
142
			goto error;
143
		if((up = auth_getuserpasswd(nil, "%s", spec)) == nil)
144
			goto error;
145
		user = up->user;
146
		pass = up->passwd;
147
	}
148
	if((s = smprint("%s:%s", user, pass)) == nil)
149
		goto error;
150
	free(up);
151
	enc64(cred, sizeof(cred), (uchar*)s, strlen(s));
152
	memset(s, 0, strlen(s));
153
	free(s);
154
	hs->credentials = smprint("Basic %s", cred);
155
	if(hs->credentials == nil)
156
		goto error;
157
	return;
158
 
159
error:
160
	free(up);
161
	free(spec);
162
	snprint(hs->autherror, sizeof hs->autherror, "%r");
163
	fprint(2, "%s: Authentication failed: %r\n", argv0);
164
}
165
 
166
struct {
167
	char *name;									/* Case-insensitive */
168
	void (*fn)(HttpState *hs, char *value);
169
} hdrtab[] = {
170
	{ "location:", location },
171
	{ "content-type:", contenttype },
172
	{ "set-cookie:", setcookie },
173
	{ "www-authenticate:", wwwauthenticate },
174
};
175
 
176
static int
177
httprcode(HttpState *hs)
178
{
179
	int n;
180
	char *p;
181
	char buf[256];
182
 
183
	n = readline(&hs->b, buf, sizeof(buf)-1);
184
	if(n <= 0)
185
		return n;
186
	if(httpdebug)
187
		fprint(2, "-> %s\n", buf);
188
	p = strchr(buf, ' ');
189
	if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){
190
		werrstr("bad response from server");
191
		return -1;
192
	}
193
	buf[n] = 0;
194
	return atoi(p+1);
195
}
196
 
197
/*
198
 *  read a single mime header, collect continuations.
199
 *
200
 *  this routine assumes that there is a blank line twixt
201
 *  the header and the message body, otherwise bytes will
202
 *  be lost.
203
 */
204
static int
205
getheader(HttpState *hs, char *buf, int n)
206
{
207
	char *p, *e;
208
	int i;
209
 
210
	n--;
211
	p = buf;
212
	for(e = p + n; ; p += i){
213
		i = readline(&hs->b, p, e-p);
214
		if(i < 0)
215
			return i;
216
 
217
		if(p == buf){
218
			/* first line */
219
			if(strchr(buf, ':') == nil)
220
				break;		/* end of headers */
221
		} else {
222
			/* continuation line */
223
			if(*p != ' ' && *p != '\t'){
224
				unreadline(&hs->b, p);
225
				*p = 0;
226
				break;		/* end of this header */
227
			}
228
		}
229
	}
230
 
231
	if(httpdebug)
232
		fprint(2, "-> %s\n", buf);
233
	return p-buf;
234
}
235
 
236
static int
237
httpheaders(HttpState *hs)
238
{
239
	char buf[2048];
240
	char *p;
241
	int i, n;
242
 
243
	for(;;){
244
		n = getheader(hs, buf, sizeof(buf));
245
		if(n < 0)
246
			return -1;
247
		if(n == 0)
248
			return 0;
249
		//	print("http header: '%.*s'\n", n, buf);
250
		for(i = 0; i < nelem(hdrtab); i++){
251
			n = strlen(hdrtab[i].name);
252
			if(cistrncmp(buf, hdrtab[i].name, n) == 0){
253
				/* skip field name and leading white */
254
				p = buf + n;
255
				while(*p == ' ' || *p == '\t')
256
					p++;
257
				(*hdrtab[i].fn)(hs, p);
258
				break;
259
			}
260
		}
261
	}
262
}
263
 
264
int
265
httpopen(Client *c, Url *url)
266
{
267
	int fd, code, redirect, authenticate;
268
	char *cookies;
269
	Ioproc *io;
270
	HttpState *hs;
271
	char *service;
272
 
273
	if(httpdebug)
274
		fprint(2, "httpopen\n");
275
	io = c->io;
276
	hs = emalloc(sizeof(*hs));
277
	hs->c = c;
278
 
279
	if(url->port)
280
		service = url->port;
281
	else
282
		service = url->scheme;
283
	hs->netaddr = estrdup(netmkaddr(url->host, 0, service));
284
	c->aux = hs;
285
	if(httpdebug){
286
		fprint(2, "dial %s\n", hs->netaddr);
287
		fprint(2, "dial port: %s\n", url->port);
288
	}
289
	fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps);
290
	if(fd < 0){
291
	Error:
292
		if(httpdebug)
293
			fprint(2, "iodial: %r\n");
294
		free(hs->location);
295
		free(hs->setcookie);
296
		free(hs->netaddr);
297
		free(hs->credentials);
298
		if(fd >= 0)
299
			ioclose(io, hs->fd);
300
		hs->fd = -1;
301
		free(hs);
302
		c->aux = nil;
303
		return -1;
304
	}
305
	hs->fd = fd;
306
	if(httpdebug)
307
		fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n",
308
			c->havepostbody? "POST": "GET", url->http.page_spec, url->host);
309
	ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n",
310
		c->havepostbody? "POST" : "GET", url->http.page_spec, url->host);
311
	if(httpdebug)
312
		fprint(2, "<- User-Agent: %s\n", c->ctl.useragent);
313
	if(c->ctl.useragent)
314
		ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent);
315
	if(c->ctl.sendcookies){
316
		/* should we use url->page here?  sometimes it is nil. */
317
		cookies = httpcookies(url->host, url->http.page_spec,
318
			url->ischeme == UShttps);
319
		if(cookies && cookies[0])
320
			ioprint(io, fd, "%s", cookies);
321
		if(httpdebug)
322
			fprint(2, "<- %s", cookies);
323
		free(cookies);
324
	}
325
	if(c->havepostbody){
326
		ioprint(io, fd, "Content-type: %s\r\n", PostContentType);
327
		ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody);
328
		if(httpdebug){
329
			fprint(2, "<- Content-type: %s\n", PostContentType);
330
			fprint(2, "<- Content-length: %ud\n", c->npostbody);
331
		}
332
	}
333
	if(c->authenticate){
334
		ioprint(io, fd, "Authorization: %s\r\n", c->authenticate);
335
		if(httpdebug)
336
			fprint(2, "<- Authorization: %s\n", c->authenticate);
337
	}
338
	ioprint(io, fd, "\r\n");
339
	if(c->havepostbody)
340
		if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody)
341
			goto Error;
342
 
343
	redirect = 0;
344
	authenticate = 0;
345
	initibuf(&hs->b, io, fd);
346
	code = httprcode(hs);
347
 
348
	switch(code){
349
	case -1:	/* connection timed out */
350
		goto Error;
351
 
352
/*
353
	case Eof:
354
		werrstr("EOF from HTTP server");
355
		goto Error;
356
*/
357
 
358
	case 200:	/* OK */
359
	case 201:	/* Created */
360
	case 202:	/* Accepted */
361
	case 204:	/* No Content */
362
	case 205: /* Reset Content */
363
#ifdef NOT_DEFINED
364
		if(ofile == nil && r->start != 0)
365
			sysfatal("page changed underfoot");
366
#endif
367
		break;
368
 
369
	case 206:	/* Partial Content */
370
		werrstr("Partial Content (206)");
371
		goto Error;
372
 
373
	case 301:	/* Moved Permanently */
374
	case 302:	/* Moved Temporarily */
375
	case 303:	/* See Other */
376
	case 307: /* Temporary Redirect  */
377
		redirect = 1;
378
		break;
379
 
380
	case 304:	/* Not Modified */
381
		break;
382
 
383
	case 400:	/* Bad Request */
384
		werrstr("Bad Request (400)");
385
		goto Error;
386
 
387
	case 401:	/* Unauthorized */
388
		if(c->authenticate){
389
			werrstr("Authentication failed (401)");
390
			goto Error;
391
		}
392
		authenticate = 1;
393
		break;
394
	case 402:	/* Payment Required */
395
		werrstr("Payment Required (402)");
396
		goto Error;
397
 
398
	case 403:	/* Forbidden */
399
		werrstr("Forbidden by server (403)");
400
		goto Error;
401
 
402
	case 404:	/* Not Found */
403
		werrstr("Not found on server (404)");
404
		goto Error;
405
 
406
	case 405:	/* Method Not Allowed  */
407
		werrstr("Method not allowed (405)");
408
		goto Error;
409
 
410
	case 406: /* Not Acceptable */
411
		werrstr("Not Acceptable (406)");
412
		goto Error;
413
 
414
	case 407:	/* Proxy auth */
415
		werrstr("Proxy authentication required (407)");
416
		goto Error;
417
 
418
	case 408: /* Request Timeout */
419
		werrstr("Request Timeout (408)");
420
		goto Error;
421
 
422
	case 409: /* Conflict */
423
		werrstr("Conflict  (409)");
424
		goto Error;
425
 
426
	case 410: /* Gone */
427
		werrstr("Gone  (410)");
428
		goto Error;
429
 
430
	case 411: /* Length Required */
431
		werrstr("Length Required  (411)");
432
		goto Error;
433
 
434
	case 412: /* Precondition Failed */
435
		werrstr("Precondition Failed  (412)");
436
		goto Error;
437
 
438
	case 413: /* Request Entity Too Large */
439
		werrstr("Request Entity Too Large  (413)");
440
		goto Error;
441
 
442
	case 414: /* Request-URI Too Long */
443
		werrstr("Request-URI Too Long  (414)");
444
		goto Error;
445
 
446
	case 415: /* Unsupported Media Type */
447
		werrstr("Unsupported Media Type  (415)");
448
		goto Error;
449
 
450
	case 416: /* Requested Range Not Satisfiable */
451
		werrstr("Requested Range Not Satisfiable  (416)");
452
		goto Error;
453
 
454
	case 417: /* Expectation Failed */
455
		werrstr("Expectation Failed  (417)");
456
		goto Error;
457
 
458
	case 500:	/* Internal server error */
459
		werrstr("Server choked (500)");
460
		goto Error;
461
 
462
	case 501:	/* Not implemented */
463
		werrstr("Server can't do it (501)");
464
		goto Error;
465
 
466
	case 502:	/* Bad gateway */
467
		werrstr("Bad gateway (502)");
468
		goto Error;
469
 
470
	case 503:	/* Service unavailable */
471
		werrstr("Service unavailable (503)");
472
		goto Error;
473
 
474
	default:
475
		/* Bogus: we should treat unknown code XYZ as code X00 */
476
		werrstr("Unknown response code %d", code);
477
		goto Error;
478
	}
479
 
480
	if(httpheaders(hs) < 0)
481
		goto Error;
482
	if(c->ctl.acceptcookies && hs->setcookie)
483
		httpsetcookie(hs->setcookie, url->host, url->path);
484
	if(authenticate){
485
		if(!hs->credentials){
486
			if(hs->autherror[0])
487
				werrstr("%s", hs->autherror);
488
			else
489
				werrstr("unauthorized; no www-authenticate: header");
490
			goto Error;
491
		}
492
		c->authenticate = hs->credentials;
493
		hs->credentials = nil;
494
	}else if(c->authenticate)
495
		c->authenticate = 0;
496
	if(redirect){
497
		if(!hs->location){
498
			werrstr("redirection without Location: header");
499
			goto Error;
500
		}
501
		c->redirect = hs->location;
502
		hs->location = nil;
503
	}
504
	return 0;
505
}
506
 
507
int
508
httpread(Client *c, Req *r)
509
{
510
	HttpState *hs;
511
	long n;
512
 
513
	hs = c->aux;
514
	n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count);
515
	if(n < 0)
516
		return -1;
517
 
518
	r->ofcall.count = n;
519
	return 0;
520
}
521
 
522
void
523
httpclose(Client *c)
524
{
525
	HttpState *hs;
526
 
527
	hs = c->aux;
528
	if(hs == nil)
529
		return;
530
	if(hs->fd >= 0)
531
		ioclose(c->io, hs->fd);
532
	hs->fd = -1;
533
	free(hs->location);
534
	free(hs->setcookie);
535
	free(hs->netaddr);
536
	free(hs->credentials);
537
	free(hs);
538
	c->aux = nil;
539
}