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