xref: /plan9/sys/src/libhttpd/parse.c (revision b39189fd423aed869c5cf5189bc504918cff969b)
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
hreqcleanup(HConnect * c)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
mimeconnection(Hlex * h,char *)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
hparseheaders(HConnect * c,int timeout)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*
mimeparams(Hlex * h)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*
mimehfields(Hlex * h)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*
mimeok(Hlex * h,char * name,int multipart,HContent * head)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*
mimeetag(Hlex * h,HETag * head)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*
mimeranges(Hlex * h,HRange * head)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
mimeaccept(Hlex * h,char * name)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
mimeacceptchar(Hlex * h,char * name)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
mimeacceptenc(Hlex * h,char * name)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
mimeacceptlang(Hlex * h,char * name)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
mimemodified(Hlex * h,char *)509 mimemodified(Hlex *h, char *)
510 {
511 	lexhead(h);
512 	h->c->head.ifmodsince = hdate2sec(h->wordval);
513 }
514 
515 static void
mimeunmodified(Hlex * h,char *)516 mimeunmodified(Hlex *h, char *)
517 {
518 	lexhead(h);
519 	h->c->head.ifunmodsince = hdate2sec(h->wordval);
520 }
521 
522 static void
mimematch(Hlex * h,char *)523 mimematch(Hlex *h, char *)
524 {
525 	h->c->head.ifmatch = mimeetag(h, h->c->head.ifmatch);
526 }
527 
528 static void
mimenomatch(Hlex * h,char *)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
mimeifrange(Hlex * h,char *)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
mimerange(Hlex * h,char *)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
authdigest(Hlex * h,char *)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
authbasic(Hlex * h,char *)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
mimeauthorization(Hlex * h,char *)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
mimeagent(Hlex * h,char *)658 mimeagent(Hlex *h, char *)
659 {
660 	lexhead(h);
661 	h->c->head.client = hstrdup(h->c, h->wordval);
662 }
663 
664 static void
mimefrom(Hlex * h,char *)665 mimefrom(Hlex *h, char *)
666 {
667 	lexhead(h);
668 }
669 
670 static void
mimehost(Hlex * h,char *)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
mimecontlen(Hlex * h,char *)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
mimeexpect(Hlex * h,char *)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
mimetransenc(Hlex * h,char *)716 mimetransenc(Hlex *h, char *)
717 {
718 	h->c->head.transenc = mimehfields(h);
719 }
720 
721 static void
mimecookie(Hlex * h,char *)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
mimefresh(Hlex * h,char *)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
mimeignore(Hlex * h,char *)757 mimeignore(Hlex *h, char *)
758 {
759 	lexhead(h);
760 }
761 
762 static void
parsejump(Hlex * h,char * k)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
lex(Hlex * h)785 lex(Hlex *h)
786 {
787 	return h->tok = lex1(h, 0);
788 }
789 
790 static int
lexbase64(Hlex * h)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
lex1(Hlex * h,int skipwhite)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
lexhead(Hlex * h)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
word(Hlex * h,char * stop)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
wordcr(Hlex * h)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
wordnl(Hlex * h)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
getc(Hlex * h)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
ungetc(Hlex * h)1009 ungetc(Hlex *h)
1010 {
1011 	if(h->eoh)
1012 		return;
1013 	h->c->hpos--;
1014 }
1015 
1016 static ulong
digtoul(char * s,char ** e)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
http11(HConnect * c)1043 http11(HConnect *c)
1044 {
1045 	return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0;
1046 }
1047 
1048 char*
hmkmimeboundary(HConnect * c)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*
hmkspairs(HConnect * c,char * s,char * t,HSPairs * next)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*
hrevspairs(HSPairs * sp)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*
hmkhfields(HConnect * c,char * s,HSPairs * p,HFields * next)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*
hrevhfields(HFields * hf)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*
hmkcontent(HConnect * c,char * generic,char * specific,HContent * next)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 }
1127