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 <ctype.h>
4
#include <libsec.h>
5
#include <bin.h>
6
#include <httpd.h>
7
#include "escape.h"
8
 
9
typedef struct Hlex	Hlex;
10
typedef struct MimeHead	MimeHead;
11
 
12
enum
13
{
14
	/*
15
	 * tokens
16
	 */
17
	Word	= 1,
18
	QString,
19
};
20
 
21
#define UlongMax	4294967295UL
22
 
23
struct Hlex
24
{
25
	int	tok;
26
	int	eoh;
27
	int	eol;			/* end of header line encountered? */
28
	uchar	*hstart;		/* start of header */
29
	jmp_buf	jmp;			/* jmp here to parse header */
30
	char	wordval[HMaxWord];
31
	HConnect *c;
32
};
33
 
34
struct MimeHead
35
{
36
	char	*name;
37
	void	(*parse)(Hlex*, char*);
38
	uchar	seen;
39
	uchar	ignore;
40
};
41
 
42
static void	mimeaccept(Hlex*, char*);
43
static void	mimeacceptchar(Hlex*, char*);
44
static void	mimeacceptenc(Hlex*, char*);
45
static void	mimeacceptlang(Hlex*, char*);
46
static void	mimeagent(Hlex*, char*);
47
static void	mimeauthorization(Hlex*, char*);
48
static void	mimeconnection(Hlex*, char*);
49
static void	mimecontlen(Hlex*, char*);
50
static void	mimecookie(Hlex*, char*);
51
static void	mimeexpect(Hlex*, char*);
52
static void	mimefresh(Hlex*, char*);
53
static void	mimefrom(Hlex*, char*);
54
static void	mimehost(Hlex*, char*);
55
static void	mimeifrange(Hlex*, char*);
56
static void	mimeignore(Hlex*, char*);
57
static void	mimematch(Hlex*, char*);
58
static void	mimemodified(Hlex*, char*);
59
static void	mimenomatch(Hlex*, char*);
60
static void	mimerange(Hlex*, char*);
61
static void	mimetransenc(Hlex*, char*);
62
static void	mimeunmodified(Hlex*, char*);
63
 
64
/*
65
 * headers seen also include
66
 * allow  cache-control chargeto
67
 * content-encoding content-language content-location content-md5 content-range content-type
68
 * date etag expires forwarded last-modified max-forwards pragma
69
 * proxy-agent proxy-authorization proxy-connection
70
 * ua-color ua-cpu ua-os ua-pixels
71
 * upgrade via x-afs-tokens x-serial-number
72
 */
73
static MimeHead	mimehead[] =
74
{
75
	{"accept",		mimeaccept},
76
	{"accept-charset",	mimeacceptchar},
77
	{"accept-encoding",	mimeacceptenc},
78
	{"accept-language",	mimeacceptlang},
79
	{"authorization",	mimeauthorization},
80
	{"connection",		mimeconnection},
81
	{"content-length",	mimecontlen},
82
	{"cookie",		mimecookie},
83
	{"expect",		mimeexpect},
84
	{"fresh",		mimefresh},
85
	{"from",		mimefrom},
86
	{"host",		mimehost},
87
	{"if-match",		mimematch},
88
	{"if-modified-since",	mimemodified},
89
	{"if-none-match",	mimenomatch},
90
	{"if-range",		mimeifrange},
91
	{"if-unmodified-since",	mimeunmodified},
92
	{"range",		mimerange},
93
	{"transfer-encoding",	mimetransenc},
94
	{"user-agent",		mimeagent},
95
};
96
 
97
char*		hmydomain;
98
char*		hversion = "HTTP/1.1";
99
 
100
static	void	lexhead(Hlex*);
101
static	void	parsejump(Hlex*, char*);
102
static	int	getc(Hlex*);
103
static	void	ungetc(Hlex*);
104
static	int	wordcr(Hlex*);
105
static	int	wordnl(Hlex*);
106
static	void	word(Hlex*, char*);
107
static	int	lex1(Hlex*, int);
108
static	int	lex(Hlex*);
109
static	int	lexbase64(Hlex*);
110
static	ulong	digtoul(char *s, char **e);
111
 
112
/*
113
 * flush and clean up junk from a request
114
 */
115
void
116
hreqcleanup(HConnect *c)
117
{
118
	int i;
119
 
120
	hxferenc(&c->hout, 0);
121
	memset(&c->req, 0, sizeof(c->req));
122
	memset(&c->head, 0, sizeof(c->head));
123
	c->hpos = c->header;
124
	c->hstop = c->header;
125
	binfree(&c->bin);
126
	for(i = 0; i < nelem(mimehead); i++){
127
		mimehead[i].seen = 0;
128
		mimehead[i].ignore = 0;
129
	}
130
}
131
 
132
/*
133
 * list of tokens
134
 * if the client is HTTP/1.0,
135
 * ignore headers which match one of the tokens.
136
 * restarts parsing if necessary.
137
 */
138
static void
139
mimeconnection(Hlex *h, char *)
140
{
141
	char *u, *p;
142
	int reparse, i;
143
 
144
	reparse = 0;
145
	for(;;){
146
		while(lex(h) != Word)
147
			if(h->tok != ',')
148
				goto breakout;
149
 
150
		if(cistrcmp(h->wordval, "keep-alive") == 0)
151
			h->c->head.persist = 1;
152
		else if(cistrcmp(h->wordval, "close") == 0)
153
			h->c->head.closeit = 1;
154
		else if(!http11(h->c)){
155
			for(i = 0; i < nelem(mimehead); i++){
156
				if(cistrcmp(mimehead[i].name, h->wordval) == 0){
157
					reparse = mimehead[i].seen && !mimehead[i].ignore;
158
					mimehead[i].ignore = 1;
159
					if(cistrcmp(mimehead[i].name, "authorization") == 0){
160
						h->c->head.authuser = nil;
161
						h->c->head.authpass = nil;
162
					}
163
				}
164
			}
165
		}
166
 
167
		if(lex(h) != ',')
168
			break;
169
	}
170
 
171
breakout:;
172
	/*
173
	 * if need to ignore headers we've already parsed,
174
	 * reset & start over.  need to save authorization
175
	 * info because it's written over when parsed.
176
	 */
177
	if(reparse){
178
		u = h->c->head.authuser;
179
		p = h->c->head.authpass;
180
		memset(&h->c->head, 0, sizeof(h->c->head));
181
		h->c->head.authuser = u;
182
		h->c->head.authpass = p;
183
 
184
		h->c->hpos = h->hstart;
185
		longjmp(h->jmp, 1);
186
	}
187
}
188
 
189
int
190
hparseheaders(HConnect *c, int timeout)
191
{
192
	Hlex h;
193
 
194
	c->head.fresh_thresh = 0;
195
	c->head.fresh_have = 0;
196
	c->head.persist = 0;
197
	if(c->req.vermaj == 0){
198
		c->head.host = hmydomain;
199
		return 1;
200
	}
201
 
202
	memset(&h, 0, sizeof(h));
203
	h.c = c;
204
	if(timeout)
205
		alarm(timeout);
206
	if(hgethead(c, 1) < 0)
207
		return -1;
208
	if(timeout)
209
		alarm(0);
210
	h.hstart = c->hpos;
211
 
212
	if(setjmp(h.jmp) == -1)
213
		return -1;
214
 
215
	h.eol = 0;
216
	h.eoh = 0;
217
	h.tok = '\n';
218
	while(lex(&h) != '\n'){
219
		if(h.tok == Word && lex(&h) == ':')
220
			parsejump(&h, hstrdup(c, h.wordval));
221
		while(h.tok != '\n')
222
			lex(&h);
223
		h.eol = h.eoh;
224
	}
225
 
226
	if(http11(c)){
227
		/*
228
		 * according to the http/1.1 spec,
229
		 * these rules must be followed
230
		 */
231
		if(c->head.host == nil){
232
			hfail(c, HBadReq, nil);
233
			return -1;
234
		}
235
		if(c->req.urihost != nil)
236
			c->head.host = c->req.urihost;
237
		/*
238
		 * also need to check host is actually this one
239
		 */
240
	}else if(c->head.host == nil)
241
		c->head.host = hmydomain;
242
	return 1;
243
}
244
 
245
/*
246
 * mimeparams	: | mimeparams ";" mimepara
247
 * mimeparam	: token "=" token | token "=" qstring
248
 */
249
static HSPairs*
250
mimeparams(Hlex *h)
251
{
252
	HSPairs *p;
253
	char *s;
254
 
255
	p = nil;
256
	for(;;){
257
		if(lex(h) != Word)
258
			break;
259
		s = hstrdup(h->c, h->wordval);
260
		if(lex(h) != Word && h->tok != QString)
261
			break;
262
		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
263
	}
264
	return hrevspairs(p);
265
}
266
 
267
/*
268
 * mimehfields	: mimehfield | mimehfields commas mimehfield
269
 * mimehfield	: token mimeparams
270
 * commas	: "," | commas ","
271
 */
272
static HFields*
273
mimehfields(Hlex *h)
274
{
275
	HFields *f;
276
 
277
	f = nil;
278
	for(;;){
279
		while(lex(h) != Word)
280
			if(h->tok != ',')
281
				goto breakout;
282
 
283
		f = hmkhfields(h->c, hstrdup(h->c, h->wordval), nil, f);
284
 
285
		if(lex(h) == ';')
286
			f->params = mimeparams(h);
287
		if(h->tok != ',')
288
			break;
289
	}
290
breakout:;
291
	return hrevhfields(f);
292
}
293
 
294
/*
295
 * parse a list of acceptable types, encodings, languages, etc.
296
 */
297
static HContent*
298
mimeok(Hlex *h, char *name, int multipart, HContent *head)
299
{
300
	char *generic, *specific, *s;
301
	float v;
302
 
303
	/*
304
	 * each type is separated by one or more commas
305
	 */
306
	while(lex(h) != Word)
307
		if(h->tok != ',')
308
			return head;
309
 
310
	generic = hstrdup(h->c, h->wordval);
311
	lex(h);
312
	if(h->tok == '/' || multipart){
313
		/*
314
		 * at one time, IE5 improperly said '*' for single types
315
		 */
316
		if(h->tok != '/')
317
			return nil;
318
		if(lex(h) != Word)
319
			return head;
320
		specific = hstrdup(h->c, h->wordval);
321
		if(!multipart && strcmp(specific, "*") != 0)
322
			return head;
323
		lex(h);
324
	}else
325
		specific = nil;
326
	head = hmkcontent(h->c, generic, specific, head);
327
 
328
	for(;;){
329
		switch(h->tok){
330
		case ';':
331
			/*
332
			 * should make a list of these params
333
			 * for accept, they fall into two classes:
334
			 *	up to a q=..., they modify the media type.
335
			 *	afterwards, they acceptance criteria
336
			 */
337
			if(lex(h) == Word){
338
				s = hstrdup(h->c, h->wordval);
339
				if(lex(h) != '=' || lex(h) != Word && h->tok != QString)
340
					return head;
341
				v = strtod(h->wordval, nil);
342
				if(strcmp(s, "q") == 0)
343
					head->q = v;
344
				else if(strcmp(s, "mxb") == 0)
345
					head->mxb = v;
346
				else{
347
					/* cope with accept: application/xhtml+xml; profile=http://www.wapforum.org/xhtml, */
348
					while(lex(h) == Word || (h->tok != ',' && h->eol == 0) )
349
						;
350
					return mimeok(h, name, multipart, head);
351
				}
352
			}
353
			break;
354
		case ',':
355
			return  mimeok(h, name, multipart, head);
356
		default:
357
			return head;
358
		}
359
		lex(h);
360
	}
361
}
362
 
363
/*
364
 * parse a list of entity tags
365
 * 1#entity-tag
366
 * entity-tag = [weak] opaque-tag
367
 * weak = "W/"
368
 * opaque-tag = quoted-string
369
 */
370
static HETag*
371
mimeetag(Hlex *h, HETag *head)
372
{
373
	HETag *e;
374
	int weak;
375
 
376
	for(;;){
377
		while(lex(h) != Word && h->tok != QString)
378
			if(h->tok != ',')
379
				return head;
380
 
381
		weak = 0;
382
		if(h->tok == Word && strcmp(h->wordval, "*") != 0){
383
			if(strcmp(h->wordval, "W") != 0)
384
				return head;
385
			if(lex(h) != '/' || lex(h) != QString)
386
				return head;
387
			weak = 1;
388
		}
389
 
390
		e = halloc(h->c, sizeof(HETag));
391
		e->etag = hstrdup(h->c, h->wordval);
392
		e->weak = weak;
393
		e->next = head;
394
		head = e;
395
 
396
		if(lex(h) != ',')
397
			return head;
398
	}
399
}
400
 
401
/*
402
 * ranges-specifier = byte-ranges-specifier
403
 * byte-ranges-specifier = "bytes" "=" byte-range-set
404
 * byte-range-set = 1#(byte-range-spec|suffix-byte-range-spec)
405
 * byte-range-spec = byte-pos "-" [byte-pos]
406
 * byte-pos = 1*DIGIT
407
 * suffix-byte-range-spec = "-" suffix-length
408
 * suffix-length = 1*DIGIT
409
 *
410
 * syntactically invalid range specifiers cause the
411
 * entire header field to be ignored.
412
 * it is syntactically incorrect for the second byte pos
413
 * to be smaller than the first byte pos
414
 */
415
static HRange*
416
mimeranges(Hlex *h, HRange *head)
417
{
418
	HRange *r, *rh, *tail;
419
	char *w;
420
	ulong start, stop;
421
	int suf;
422
 
423
	if(lex(h) != Word || strcmp(h->wordval, "bytes") != 0 || lex(h) != '=')
424
		return head;
425
 
426
	rh = nil;
427
	tail = nil;
428
	for(;;){
429
		while(lex(h) != Word){
430
			if(h->tok != ','){
431
				if(h->tok == '\n')
432
					goto breakout;
433
				return head;
434
			}
435
		}
436
 
437
		w = h->wordval;
438
		start = 0;
439
		suf = 1;
440
		if(w[0] != '-'){
441
			suf = 0;
442
			start = digtoul(w, &w);
443
			if(w[0] != '-')
444
				return head;
445
		}
446
		w++;
447
		stop = ~0UL;
448
		if(w[0] != '\0'){
449
			stop = digtoul(w, &w);
450
			if(w[0] != '\0')
451
				return head;
452
			if(!suf && stop < start)
453
				return head;
454
		}
455
 
456
		r = halloc(h->c, sizeof(HRange));
457
		r->suffix = suf;
458
		r->start = start;
459
		r->stop = stop;
460
		r->next = nil;
461
		if(rh == nil)
462
			rh = r;
463
		else
464
			tail->next = r;
465
		tail = r;
466
 
467
		if(lex(h) != ','){
468
			if(h->tok == '\n')
469
				break;
470
			return head;
471
		}
472
	}
473
breakout:;
474
 
475
	if(head == nil)
476
		return rh;
477
 
478
	for(tail = head; tail->next != nil; tail = tail->next)
479
		;
480
	tail->next = rh;
481
	return head;
482
}
483
 
484
static void
485
mimeaccept(Hlex *h, char *name)
486
{
487
	h->c->head.oktype = mimeok(h, name, 1, h->c->head.oktype);
488
}
489
 
490
static void
491
mimeacceptchar(Hlex *h, char *name)
492
{
493
	h->c->head.okchar = mimeok(h, name, 0, h->c->head.okchar);
494
}
495
 
496
static void
497
mimeacceptenc(Hlex *h, char *name)
498
{
499
	h->c->head.okencode = mimeok(h, name, 0, h->c->head.okencode);
500
}
501
 
502
static void
503
mimeacceptlang(Hlex *h, char *name)
504
{
505
	h->c->head.oklang = mimeok(h, name, 0, h->c->head.oklang);
506
}
507
 
508
static void
509
mimemodified(Hlex *h, char *)
510
{
511
	lexhead(h);
512
	h->c->head.ifmodsince = hdate2sec(h->wordval);
513
}
514
 
515
static void
516
mimeunmodified(Hlex *h, char *)
517
{
518
	lexhead(h);
519
	h->c->head.ifunmodsince = hdate2sec(h->wordval);
520
}
521
 
522
static void
523
mimematch(Hlex *h, char *)
524
{
525
	h->c->head.ifmatch = mimeetag(h, h->c->head.ifmatch);
526
}
527
 
528
static void
529
mimenomatch(Hlex *h, char *)
530
{
531
	h->c->head.ifnomatch = mimeetag(h, h->c->head.ifnomatch);
532
}
533
 
534
/*
535
 * argument is either etag or date
536
 */
537
static void
538
mimeifrange(Hlex *h, char *)
539
{
540
	int c, d, et;
541
 
542
	et = 0;
543
	c = getc(h);
544
	while(c == ' ' || c == '\t')
545
		c = getc(h);
546
	if(c == '"')
547
		et = 1;
548
	else if(c == 'W'){
549
		d = getc(h);
550
		if(d == '/')
551
			et = 1;
552
		ungetc(h);
553
	}
554
	ungetc(h);
555
	if(et){
556
		h->c->head.ifrangeetag = mimeetag(h, h->c->head.ifrangeetag);
557
	}else{
558
		lexhead(h);
559
		h->c->head.ifrangedate = hdate2sec(h->wordval);
560
	}
561
}
562
 
563
static void
564
mimerange(Hlex *h, char *)
565
{
566
	h->c->head.range = mimeranges(h, h->c->head.range);
567
}
568
 
569
/*
570
 * parse it like cookies
571
 */
572
static void
573
authdigest(Hlex *h, char *)
574
{
575
	char *s;
576
	HSPairs *p;
577
 
578
	p = nil;
579
	for(;;){
580
		while(lex(h) != Word)
581
			if(h->tok != ';' && h->tok != ',')
582
				goto breakout;
583
		s = hstrdup(h->c, h->wordval);
584
		while (lex(h) != Word && h->tok != QString)
585
			if (h->tok != '=')
586
				goto breakout;
587
		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
588
	}
589
breakout:
590
	h->c->head.authinfo = hrevspairs(p);
591
}
592
 
593
/*
594
 * note: netscape and ie through versions 4.7 and 4
595
 * support only basic authorization, so that is all that is supported here
596
 *
597
 * "Authorization" ":" "Basic" base64-user-pass
598
 * where base64-user-pass is the base64 encoding of
599
 * username ":" password
600
 */
601
static void
602
authbasic(Hlex *h, char *)
603
{
604
	char *up, *p;
605
	int n;
606
 
607
	n = lexbase64(h);
608
	if(!n)
609
		return;
610
 
611
	/*
612
	 * wipe out source for password, so it won't be logged.
613
	 * it is replaced by a single =,
614
	 * which is valid base64, but not ok for an auth reponse.
615
	 * therefore future parses of the header field will not overwrite
616
	 * authuser and authpass.
617
	 */
618
	memmove(h->c->hpos - (n - 1), h->c->hpos, h->c->hstop - h->c->hpos);
619
	h->c->hstop -= n - 1;
620
	*h->c->hstop = '\0';
621
	h->c->hpos -= n - 1;
622
	h->c->hpos[-1] = '=';
623
 
624
	up = halloc(h->c, n + 1);
625
	n = dec64((uchar*)up, n, h->wordval, n);
626
	up[n] = '\0';
627
	p = strchr(up, ':');
628
	if(p != nil){
629
		*p++ = '\0';
630
		h->c->head.authuser = hstrdup(h->c, up);
631
		h->c->head.authpass = hstrdup(h->c, p);
632
	}
633
}
634
 
635
/*
636
 * "Authorization" ":" "Basic" | "Digest" ...
637
 */
638
static void
639
mimeauthorization(Hlex *h, char *)
640
{
641
	int i;
642
	static MimeHead authparser[] = {
643
		{ "basic", authbasic },
644
		{ "digest", authdigest },
645
	};
646
 
647
	if(lex(h) != Word)
648
		return;
649
 
650
	for (i = 0; i < nelem(authparser); i++)
651
		if (cistrcmp(h->wordval, authparser[i].name) == 0) {
652
			(*authparser[i].parse)(h, nil);
653
			break;
654
		}
655
}
656
 
657
static void
658
mimeagent(Hlex *h, char *)
659
{
660
	lexhead(h);
661
	h->c->head.client = hstrdup(h->c, h->wordval);
662
}
663
 
664
static void
665
mimefrom(Hlex *h, char *)
666
{
667
	lexhead(h);
668
}
669
 
670
static void
671
mimehost(Hlex *h, char *)
672
{
673
	char *hd;
674
 
675
	lexhead(h);
676
	for(hd = h->wordval; *hd == ' ' || *hd == '\t'; hd++)
677
		;
678
	h->c->head.host = hlower(hstrdup(h->c, hd));
679
}
680
 
681
/*
682
 * if present, implies that a message body follows the headers
683
 * "content-length" ":" digits
684
 */
685
static void
686
mimecontlen(Hlex *h, char *)
687
{
688
	char *e;
689
	ulong v;
690
 
691
	if(lex(h) != Word)
692
		return;
693
	e = h->wordval;
694
	v = digtoul(e, &e);
695
	if(v == ~0UL || *e != '\0')
696
		return;
697
	h->c->head.contlen = v;
698
}
699
 
700
/*
701
 * mimexpect	: "expect" ":" expects
702
 * expects	: | expects "," expect
703
 * expect	: "100-continue" | token | token "=" token expectparams | token "=" qstring expectparams
704
 * expectparams	: ";" token | ";" token "=" token | token "=" qstring
705
 * for now, we merely parse "100-continue" or anything else.
706
 */
707
static void
708
mimeexpect(Hlex *h, char *)
709
{
710
	if(lex(h) != Word || cistrcmp(h->wordval, "100-continue") != 0 || lex(h) != '\n')
711
		h->c->head.expectother = 1;
712
	h->c->head.expectcont = 1;
713
}
714
 
715
static void
716
mimetransenc(Hlex *h, char *)
717
{
718
	h->c->head.transenc = mimehfields(h);
719
}
720
 
721
static void
722
mimecookie(Hlex *h, char *)
723
{
724
	char *s;
725
	HSPairs *p;
726
 
727
	p = nil;
728
	for(;;){
729
		while(lex(h) != Word)
730
			if(h->tok != ';' && h->tok != ',')
731
				goto breakout;
732
		s = hstrdup(h->c, h->wordval);
733
		while (lex(h) != Word && h->tok != QString)
734
			if (h->tok != '=')
735
				goto breakout;
736
		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
737
	}
738
breakout:
739
	h->c->head.cookie = hrevspairs(p);
740
}
741
 
742
static void
743
mimefresh(Hlex *h, char *)
744
{
745
	char *s;
746
 
747
	lexhead(h);
748
	for(s = h->wordval; *s && (*s==' ' || *s=='\t'); s++)
749
		;
750
	if(strncmp(s, "pathstat/", 9) == 0)
751
		h->c->head.fresh_thresh = atoi(s+9);
752
	else if(strncmp(s, "have/", 5) == 0)
753
		h->c->head.fresh_have = atoi(s+5);
754
}
755
 
756
static void
757
mimeignore(Hlex *h, char *)
758
{
759
	lexhead(h);
760
}
761
 
762
static void
763
parsejump(Hlex *h, char *k)
764
{
765
	int l, r, m;
766
 
767
	l = 1;
768
	r = nelem(mimehead) - 1;
769
	while(l <= r){
770
		m = (r + l) >> 1;
771
		if(cistrcmp(mimehead[m].name, k) <= 0)
772
			l = m + 1;
773
		else
774
			r = m - 1;
775
	}
776
	m = l - 1;
777
	if(cistrcmp(mimehead[m].name, k) == 0 && !mimehead[m].ignore){
778
		mimehead[m].seen = 1;
779
		(*mimehead[m].parse)(h, k);
780
	}else
781
		mimeignore(h, k);
782
}
783
 
784
static int
785
lex(Hlex *h)
786
{
787
	return h->tok = lex1(h, 0);
788
}
789
 
790
static int
791
lexbase64(Hlex *h)
792
{
793
	int c, n;
794
 
795
	n = 0;
796
	lex1(h, 1);
797
 
798
	while((c = getc(h)) >= 0){
799
		if(!isalnum(c) && c != '+' && c != '/'){
800
			ungetc(h);
801
			break;
802
		}
803
		if(n < HMaxWord-1)
804
			h->wordval[n++] = c;
805
	}
806
	h->wordval[n] = '\0';
807
	return n;
808
}
809
 
810
/*
811
 * rfc 822/rfc 1521 lexical analyzer
812
 */
813
static int
814
lex1(Hlex *h, int skipwhite)
815
{
816
	int level, c;
817
 
818
	if(h->eol)
819
		return '\n';
820
 
821
top:
822
	c = getc(h);
823
	switch(c){
824
	case '(':
825
		level = 1;
826
		while((c = getc(h)) >= 0){
827
			if(c == '\\'){
828
				c = getc(h);
829
				if(c < 0)
830
					return '\n';
831
				continue;
832
			}
833
			if(c == '(')
834
				level++;
835
			else if(c == ')' && --level == 0)
836
				break;
837
			else if(c == '\n'){
838
				c = getc(h);
839
				if(c < 0)
840
					return '\n';
841
				if(c == ')' && --level == 0)
842
					break;
843
				if(c != ' ' && c != '\t'){
844
					ungetc(h);
845
					return '\n';
846
				}
847
			}
848
		}
849
		goto top;
850
 
851
	case ' ': case '\t':
852
		goto top;
853
 
854
	case '\r':
855
		c = getc(h);
856
		if(c != '\n'){
857
			ungetc(h);
858
			goto top;
859
		}
860
 
861
	case '\n':
862
		if(h->tok == '\n'){
863
			h->eol = 1;
864
			h->eoh = 1;
865
			return '\n';
866
		}
867
		c = getc(h);
868
		if(c < 0){
869
			h->eol = 1;
870
			return '\n';
871
		}
872
		if(c != ' ' && c != '\t'){
873
			ungetc(h);
874
			h->eol = 1;
875
			return '\n';
876
		}
877
		goto top;
878
 
879
	case ')':
880
	case '<': case '>':
881
	case '[': case ']':
882
	case '@': case '/':
883
	case ',': case ';': case ':': case '?': case '=':
884
		if(skipwhite){
885
			ungetc(h);
886
			return c;
887
		}
888
		return c;
889
 
890
	case '"':
891
		if(skipwhite){
892
			ungetc(h);
893
			return c;
894
		}
895
		word(h, "\"");
896
		getc(h);		/* skip the closing quote */
897
		return QString;
898
 
899
	default:
900
		ungetc(h);
901
		if(skipwhite)
902
			return c;
903
		word(h, "\"(){}<>@,;:/[]?=\r\n \t");
904
		if(h->wordval[0] == '\0'){
905
			h->c->head.closeit = 1;
906
			hfail(h->c, HSyntax);
907
			longjmp(h->jmp, -1);
908
		}
909
		return Word;
910
	}
911
	/* not reached */
912
}
913
 
914
/*
915
 * return the rest of an rfc 822, including \n
916
 * do not map to lower case
917
 */
918
static void
919
lexhead(Hlex *h)
920
{
921
	int c, n;
922
 
923
	n = 0;
924
	while((c = getc(h)) >= 0){
925
		if(c == '\r')
926
			c = wordcr(h);
927
		else if(c == '\n')
928
			c = wordnl(h);
929
		if(c == '\n')
930
			break;
931
		if(c == '\\'){
932
			c = getc(h);
933
			if(c < 0)
934
				break;
935
		}
936
 
937
		if(n < HMaxWord-1)
938
			h->wordval[n++] = c;
939
	}
940
	h->tok = '\n';
941
	h->eol = 1;
942
	h->wordval[n] = '\0';
943
}
944
 
945
static void
946
word(Hlex *h, char *stop)
947
{
948
	int c, n;
949
 
950
	n = 0;
951
	while((c = getc(h)) >= 0){
952
		if(c == '\r')
953
			c = wordcr(h);
954
		else if(c == '\n')
955
			c = wordnl(h);
956
		if(c == '\\'){
957
			c = getc(h);
958
			if(c < 0)
959
				break;
960
		}else if(c < 32 || strchr(stop, c) != nil){
961
			ungetc(h);
962
			break;
963
		}
964
 
965
		if(n < HMaxWord-1)
966
			h->wordval[n++] = c;
967
	}
968
	h->wordval[n] = '\0';
969
}
970
 
971
static int
972
wordcr(Hlex *h)
973
{
974
	int c;
975
 
976
	c = getc(h);
977
	if(c == '\n')
978
		return wordnl(h);
979
	ungetc(h);
980
	return ' ';
981
}
982
 
983
static int
984
wordnl(Hlex *h)
985
{
986
	int c;
987
 
988
	c = getc(h);
989
	if(c == ' ' || c == '\t')
990
		return c;
991
	ungetc(h);
992
 
993
	return '\n';
994
}
995
 
996
static int
997
getc(Hlex *h)
998
{
999
	if(h->eoh)
1000
		return -1;
1001
	if(h->c->hpos < h->c->hstop)
1002
		return *h->c->hpos++;
1003
	h->eoh = 1;
1004
	h->eol = 1;
1005
	return -1;
1006
}
1007
 
1008
static void
1009
ungetc(Hlex *h)
1010
{
1011
	if(h->eoh)
1012
		return;
1013
	h->c->hpos--;
1014
}
1015
 
1016
static ulong
1017
digtoul(char *s, char **e)
1018
{
1019
	ulong v;
1020
	int c, ovfl;
1021
 
1022
	v = 0;
1023
	ovfl = 0;
1024
	for(;;){
1025
		c = *s;
1026
		if(c < '0' || c > '9')
1027
			break;
1028
		s++;
1029
		c -= '0';
1030
		if(v > UlongMax/10 || v == UlongMax/10 && c >= UlongMax%10)
1031
			ovfl = 1;
1032
		v = v * 10 + c;
1033
	}
1034
 
1035
	if(e)
1036
		*e = s;
1037
	if(ovfl)
1038
		return UlongMax;
1039
	return v;
1040
}
1041
 
1042
int
1043
http11(HConnect *c)
1044
{
1045
	return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0;
1046
}
1047
 
1048
char*
1049
hmkmimeboundary(HConnect *c)
1050
{
1051
	char buf[32];
1052
	int i;
1053
 
1054
	srand((time(0)<<16)|getpid());
1055
	strcpy(buf, "upas-");
1056
	for(i = 5; i < sizeof(buf)-1; i++)
1057
		buf[i] = 'a' + nrand(26);
1058
	buf[i] = 0;
1059
	return hstrdup(c, buf);
1060
}
1061
 
1062
HSPairs*
1063
hmkspairs(HConnect *c, char *s, char *t, HSPairs *next)
1064
{
1065
	HSPairs *sp;
1066
 
1067
	sp = halloc(c, sizeof *sp);
1068
	sp->s = s;
1069
	sp->t = t;
1070
	sp->next = next;
1071
	return sp;
1072
}
1073
 
1074
HSPairs*
1075
hrevspairs(HSPairs *sp)
1076
{
1077
	HSPairs *last, *next;
1078
 
1079
	last = nil;
1080
	for(; sp != nil; sp = next){
1081
		next = sp->next;
1082
		sp->next = last;
1083
		last = sp;
1084
	}
1085
	return last;
1086
}
1087
 
1088
HFields*
1089
hmkhfields(HConnect *c, char *s, HSPairs *p, HFields *next)
1090
{
1091
	HFields *hf;
1092
 
1093
	hf = halloc(c, sizeof *hf);
1094
	hf->s = s;
1095
	hf->params = p;
1096
	hf->next = next;
1097
	return hf;
1098
}
1099
 
1100
HFields*
1101
hrevhfields(HFields *hf)
1102
{
1103
	HFields *last, *next;
1104
 
1105
	last = nil;
1106
	for(; hf != nil; hf = next){
1107
		next = hf->next;
1108
		hf->next = last;
1109
		last = hf;
1110
	}
1111
	return last;
1112
}
1113
 
1114
HContent*
1115
hmkcontent(HConnect *c, char *generic, char *specific, HContent *next)
1116
{
1117
	HContent *ct;
1118
 
1119
	ct = halloc(c, sizeof(HContent));
1120
	ct->generic = generic;
1121
	ct->specific = specific;
1122
	ct->next = next;
1123
	ct->q = 1;
1124
	ct->mxb = 0;
1125
	return ct;
1126
}