xref: /plan9/sys/src/libhttpd/parse.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
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  * note: netscape and ie through versions 4.7 and 4
571  * support only basic authorization, so that is all that is supported here
572  *
573  * "Authorization" ":" "Basic" base64-user-pass
574  * where base64-user-pass is the base64 encoding of
575  * username ":" password
576  */
577 static void
578 mimeauthorization(Hlex *h, char *)
579 {
580 	char *up, *p;
581 	int n;
582 
583 	if(lex(h) != Word || cistrcmp(h->wordval, "basic") != 0)
584 		return;
585 
586 	n = lexbase64(h);
587 	if(!n)
588 		return;
589 
590 	/*
591 	 * wipe out source for password, so it won't be logged.
592 	 * it is replaced by a single =,
593 	 * which is valid base64, but not ok for an auth reponse.
594 	 * therefore future parses of the header field will not overwrite
595 	 * authuser and authpass.
596 	 */
597 	memmove(h->c->hpos - (n - 1), h->c->hpos, h->c->hstop - h->c->hpos);
598 	h->c->hstop -= n - 1;
599 	*h->c->hstop = '\0';
600 	h->c->hpos -= n - 1;
601 	h->c->hpos[-1] = '=';
602 
603 	up = halloc(h->c, n + 1);
604 	n = dec64((uchar*)up, n, h->wordval, n);
605 	up[n] = '\0';
606 	p = strchr(up, ':');
607 	if(p != nil){
608 		*p++ = '\0';
609 		h->c->head.authuser = hstrdup(h->c, up);
610 		h->c->head.authpass = hstrdup(h->c, p);
611 	}
612 }
613 
614 static void
615 mimeagent(Hlex *h, char *)
616 {
617 	lexhead(h);
618 	h->c->head.client = hstrdup(h->c, h->wordval);
619 }
620 
621 static void
622 mimefrom(Hlex *h, char *)
623 {
624 	lexhead(h);
625 }
626 
627 static void
628 mimehost(Hlex *h, char *)
629 {
630 	char *hd;
631 
632 	lexhead(h);
633 	for(hd = h->wordval; *hd == ' ' || *hd == '\t'; hd++)
634 		;
635 	h->c->head.host = hlower(hstrdup(h->c, hd));
636 }
637 
638 /*
639  * if present, implies that a message body follows the headers
640  * "content-length" ":" digits
641  */
642 static void
643 mimecontlen(Hlex *h, char *)
644 {
645 	char *e;
646 	ulong v;
647 
648 	if(lex(h) != Word)
649 		return;
650 	e = h->wordval;
651 	v = digtoul(e, &e);
652 	if(v == ~0UL || *e != '\0')
653 		return;
654 	h->c->head.contlen = v;
655 }
656 
657 /*
658  * mimexpect	: "expect" ":" expects
659  * expects	: | expects "," expect
660  * expect	: "100-continue" | token | token "=" token expectparams | token "=" qstring expectparams
661  * expectparams	: ";" token | ";" token "=" token | token "=" qstring
662  * for now, we merely parse "100-continue" or anything else.
663  */
664 static void
665 mimeexpect(Hlex *h, char *)
666 {
667 	if(lex(h) != Word || cistrcmp(h->wordval, "100-continue") != 0 || lex(h) != '\n')
668 		h->c->head.expectother = 1;
669 	h->c->head.expectcont = 1;
670 }
671 
672 static void
673 mimetransenc(Hlex *h, char *)
674 {
675 	h->c->head.transenc = mimehfields(h);
676 }
677 
678 static void
679 mimecookie(Hlex *h, char *)
680 {
681 	char *s;
682 	HSPairs *p;
683 
684 	p = nil;
685 	for(;;){
686 		while(lex(h) != Word)
687 			if(h->tok != ';' && h->tok != ',')
688 				goto breakout;
689 		s = hstrdup(h->c, h->wordval);
690 		while (lex(h) != Word && h->tok != QString)
691 			if (h->tok != '=')
692 				goto breakout;
693 		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
694 	}
695 breakout:
696 	h->c->head.cookie = hrevspairs(p);
697 }
698 
699 static void
700 mimefresh(Hlex *h, char *)
701 {
702 	char *s;
703 
704 	lexhead(h);
705 	for(s = h->wordval; *s && (*s==' ' || *s=='\t'); s++)
706 		;
707 	if(strncmp(s, "pathstat/", 9) == 0)
708 		h->c->head.fresh_thresh = atoi(s+9);
709 	else if(strncmp(s, "have/", 5) == 0)
710 		h->c->head.fresh_have = atoi(s+5);
711 }
712 
713 static void
714 mimeignore(Hlex *h, char *)
715 {
716 	lexhead(h);
717 }
718 
719 static void
720 parsejump(Hlex *h, char *k)
721 {
722 	int l, r, m;
723 
724 	l = 1;
725 	r = nelem(mimehead) - 1;
726 	while(l <= r){
727 		m = (r + l) >> 1;
728 		if(cistrcmp(mimehead[m].name, k) <= 0)
729 			l = m + 1;
730 		else
731 			r = m - 1;
732 	}
733 	m = l - 1;
734 	if(cistrcmp(mimehead[m].name, k) == 0 && !mimehead[m].ignore){
735 		mimehead[m].seen = 1;
736 		(*mimehead[m].parse)(h, k);
737 	}else
738 		mimeignore(h, k);
739 }
740 
741 static int
742 lex(Hlex *h)
743 {
744 	return h->tok = lex1(h, 0);
745 }
746 
747 static int
748 lexbase64(Hlex *h)
749 {
750 	int c, n;
751 
752 	n = 0;
753 	lex1(h, 1);
754 
755 	while((c = getc(h)) >= 0){
756 		if(!isalnum(c) && c != '+' && c != '/'){
757 			ungetc(h);
758 			break;
759 		}
760 		if(n < HMaxWord-1)
761 			h->wordval[n++] = c;
762 	}
763 	h->wordval[n] = '\0';
764 	return n;
765 }
766 
767 /*
768  * rfc 822/rfc 1521 lexical analyzer
769  */
770 static int
771 lex1(Hlex *h, int skipwhite)
772 {
773 	int level, c;
774 
775 	if(h->eol)
776 		return '\n';
777 
778 top:
779 	c = getc(h);
780 	switch(c){
781 	case '(':
782 		level = 1;
783 		while((c = getc(h)) >= 0){
784 			if(c == '\\'){
785 				c = getc(h);
786 				if(c < 0)
787 					return '\n';
788 				continue;
789 			}
790 			if(c == '(')
791 				level++;
792 			else if(c == ')' && --level == 0)
793 				break;
794 			else if(c == '\n'){
795 				c = getc(h);
796 				if(c < 0)
797 					return '\n';
798 				if(c == ')' && --level == 0)
799 					break;
800 				if(c != ' ' && c != '\t'){
801 					ungetc(h);
802 					return '\n';
803 				}
804 			}
805 		}
806 		goto top;
807 
808 	case ' ': case '\t':
809 		goto top;
810 
811 	case '\r':
812 		c = getc(h);
813 		if(c != '\n'){
814 			ungetc(h);
815 			goto top;
816 		}
817 
818 	case '\n':
819 		if(h->tok == '\n'){
820 			h->eol = 1;
821 			h->eoh = 1;
822 			return '\n';
823 		}
824 		c = getc(h);
825 		if(c < 0){
826 			h->eol = 1;
827 			return '\n';
828 		}
829 		if(c != ' ' && c != '\t'){
830 			ungetc(h);
831 			h->eol = 1;
832 			return '\n';
833 		}
834 		goto top;
835 
836 	case ')':
837 	case '<': case '>':
838 	case '[': case ']':
839 	case '@': case '/':
840 	case ',': case ';': case ':': case '?': case '=':
841 		if(skipwhite){
842 			ungetc(h);
843 			return c;
844 		}
845 		return c;
846 
847 	case '"':
848 		if(skipwhite){
849 			ungetc(h);
850 			return c;
851 		}
852 		word(h, "\"");
853 		getc(h);		/* skip the closing quote */
854 		return QString;
855 
856 	default:
857 		ungetc(h);
858 		if(skipwhite)
859 			return c;
860 		word(h, "\"(){}<>@,;:/[]?=\r\n \t");
861 		if(h->wordval[0] == '\0'){
862 			h->c->head.closeit = 1;
863 			hfail(h->c, HSyntax);
864 			longjmp(h->jmp, -1);
865 		}
866 		return Word;
867 	}
868 	/* not reached */
869 }
870 
871 /*
872  * return the rest of an rfc 822, including \n
873  * do not map to lower case
874  */
875 static void
876 lexhead(Hlex *h)
877 {
878 	int c, n;
879 
880 	n = 0;
881 	while((c = getc(h)) >= 0){
882 		if(c == '\r')
883 			c = wordcr(h);
884 		else if(c == '\n')
885 			c = wordnl(h);
886 		if(c == '\n')
887 			break;
888 		if(c == '\\'){
889 			c = getc(h);
890 			if(c < 0)
891 				break;
892 		}
893 
894 		if(n < HMaxWord-1)
895 			h->wordval[n++] = c;
896 	}
897 	h->tok = '\n';
898 	h->eol = 1;
899 	h->wordval[n] = '\0';
900 }
901 
902 static void
903 word(Hlex *h, char *stop)
904 {
905 	int c, n;
906 
907 	n = 0;
908 	while((c = getc(h)) >= 0){
909 		if(c == '\r')
910 			c = wordcr(h);
911 		else if(c == '\n')
912 			c = wordnl(h);
913 		if(c == '\\'){
914 			c = getc(h);
915 			if(c < 0)
916 				break;
917 		}else if(c < 32 || strchr(stop, c) != nil){
918 			ungetc(h);
919 			break;
920 		}
921 
922 		if(n < HMaxWord-1)
923 			h->wordval[n++] = c;
924 	}
925 	h->wordval[n] = '\0';
926 }
927 
928 static int
929 wordcr(Hlex *h)
930 {
931 	int c;
932 
933 	c = getc(h);
934 	if(c == '\n')
935 		return wordnl(h);
936 	ungetc(h);
937 	return ' ';
938 }
939 
940 static int
941 wordnl(Hlex *h)
942 {
943 	int c;
944 
945 	c = getc(h);
946 	if(c == ' ' || c == '\t')
947 		return c;
948 	ungetc(h);
949 
950 	return '\n';
951 }
952 
953 static int
954 getc(Hlex *h)
955 {
956 	if(h->eoh)
957 		return -1;
958 	if(h->c->hpos < h->c->hstop)
959 		return *h->c->hpos++;
960 	h->eoh = 1;
961 	h->eol = 1;
962 	return -1;
963 }
964 
965 static void
966 ungetc(Hlex *h)
967 {
968 	if(h->eoh)
969 		return;
970 	h->c->hpos--;
971 }
972 
973 static ulong
974 digtoul(char *s, char **e)
975 {
976 	ulong v;
977 	int c, ovfl;
978 
979 	v = 0;
980 	ovfl = 0;
981 	for(;;){
982 		c = *s;
983 		if(c < '0' || c > '9')
984 			break;
985 		s++;
986 		c -= '0';
987 		if(v > UlongMax/10 || v == UlongMax/10 && c >= UlongMax%10)
988 			ovfl = 1;
989 		v = v * 10 + c;
990 	}
991 
992 	if(e)
993 		*e = s;
994 	if(ovfl)
995 		return UlongMax;
996 	return v;
997 }
998 
999 int
1000 http11(HConnect *c)
1001 {
1002 	return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0;
1003 }
1004 
1005 char*
1006 hmkmimeboundary(HConnect *c)
1007 {
1008 	char buf[32];
1009 	int i;
1010 
1011 	srand((time(0)<<16)|getpid());
1012 	strcpy(buf, "upas-");
1013 	for(i = 5; i < sizeof(buf)-1; i++)
1014 		buf[i] = 'a' + nrand(26);
1015 	buf[i] = 0;
1016 	return hstrdup(c, buf);
1017 }
1018 
1019 HSPairs*
1020 hmkspairs(HConnect *c, char *s, char *t, HSPairs *next)
1021 {
1022 	HSPairs *sp;
1023 
1024 	sp = halloc(c, sizeof *sp);
1025 	sp->s = s;
1026 	sp->t = t;
1027 	sp->next = next;
1028 	return sp;
1029 }
1030 
1031 HSPairs*
1032 hrevspairs(HSPairs *sp)
1033 {
1034 	HSPairs *last, *next;
1035 
1036 	last = nil;
1037 	for(; sp != nil; sp = next){
1038 		next = sp->next;
1039 		sp->next = last;
1040 		last = sp;
1041 	}
1042 	return last;
1043 }
1044 
1045 HFields*
1046 hmkhfields(HConnect *c, char *s, HSPairs *p, HFields *next)
1047 {
1048 	HFields *hf;
1049 
1050 	hf = halloc(c, sizeof *hf);
1051 	hf->s = s;
1052 	hf->params = p;
1053 	hf->next = next;
1054 	return hf;
1055 }
1056 
1057 HFields*
1058 hrevhfields(HFields *hf)
1059 {
1060 	HFields *last, *next;
1061 
1062 	last = nil;
1063 	for(; hf != nil; hf = next){
1064 		next = hf->next;
1065 		hf->next = last;
1066 		last = hf;
1067 	}
1068 	return last;
1069 }
1070 
1071 HContent*
1072 hmkcontent(HConnect *c, char *generic, char *specific, HContent *next)
1073 {
1074 	HContent *ct;
1075 
1076 	ct = halloc(c, sizeof(HContent));
1077 	ct->generic = generic;
1078 	ct->specific = specific;
1079 	ct->next = next;
1080 	ct->q = 1;
1081 	ct->mxb = 0;
1082 	return ct;
1083 }
1084