plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

parse.c (18786B)


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