xref: /plan9/sys/src/libhttpd/parse.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
32bef681aSDavid du Colombier #include <ctype.h>
480ee5cbfSDavid du Colombier #include <libsec.h>
580ee5cbfSDavid du Colombier #include <bin.h>
680ee5cbfSDavid du Colombier #include <httpd.h>
780ee5cbfSDavid du Colombier #include "escape.h"
880ee5cbfSDavid du Colombier 
980ee5cbfSDavid du Colombier typedef struct Hlex	Hlex;
1080ee5cbfSDavid du Colombier typedef struct MimeHead	MimeHead;
1180ee5cbfSDavid du Colombier 
1280ee5cbfSDavid du Colombier enum
1380ee5cbfSDavid du Colombier {
1480ee5cbfSDavid du Colombier 	/*
1580ee5cbfSDavid du Colombier 	 * tokens
1680ee5cbfSDavid du Colombier 	 */
1780ee5cbfSDavid du Colombier 	Word	= 1,
1880ee5cbfSDavid du Colombier 	QString,
1980ee5cbfSDavid du Colombier };
2080ee5cbfSDavid du Colombier 
2180ee5cbfSDavid du Colombier #define UlongMax	4294967295UL
2280ee5cbfSDavid du Colombier 
2380ee5cbfSDavid du Colombier struct Hlex
2480ee5cbfSDavid du Colombier {
2580ee5cbfSDavid du Colombier 	int	tok;
2680ee5cbfSDavid du Colombier 	int	eoh;
2780ee5cbfSDavid du Colombier 	int	eol;			/* end of header line encountered? */
2880ee5cbfSDavid du Colombier 	uchar	*hstart;		/* start of header */
2980ee5cbfSDavid du Colombier 	jmp_buf	jmp;			/* jmp here to parse header */
3080ee5cbfSDavid du Colombier 	char	wordval[HMaxWord];
3180ee5cbfSDavid du Colombier 	HConnect *c;
3280ee5cbfSDavid du Colombier };
3380ee5cbfSDavid du Colombier 
3480ee5cbfSDavid du Colombier struct MimeHead
3580ee5cbfSDavid du Colombier {
3680ee5cbfSDavid du Colombier 	char	*name;
3780ee5cbfSDavid du Colombier 	void	(*parse)(Hlex*, char*);
3880ee5cbfSDavid du Colombier 	uchar	seen;
3980ee5cbfSDavid du Colombier 	uchar	ignore;
4080ee5cbfSDavid du Colombier };
4180ee5cbfSDavid du Colombier 
4280ee5cbfSDavid du Colombier static void	mimeaccept(Hlex*, char*);
4380ee5cbfSDavid du Colombier static void	mimeacceptchar(Hlex*, char*);
4480ee5cbfSDavid du Colombier static void	mimeacceptenc(Hlex*, char*);
4580ee5cbfSDavid du Colombier static void	mimeacceptlang(Hlex*, char*);
4680ee5cbfSDavid du Colombier static void	mimeagent(Hlex*, char*);
4780ee5cbfSDavid du Colombier static void	mimeauthorization(Hlex*, char*);
4880ee5cbfSDavid du Colombier static void	mimeconnection(Hlex*, char*);
4980ee5cbfSDavid du Colombier static void	mimecontlen(Hlex*, char*);
502bef681aSDavid du Colombier static void	mimecookie(Hlex*, char*);
5180ee5cbfSDavid du Colombier static void	mimeexpect(Hlex*, char*);
5280ee5cbfSDavid du Colombier static void	mimefresh(Hlex*, char*);
5380ee5cbfSDavid du Colombier static void	mimefrom(Hlex*, char*);
5480ee5cbfSDavid du Colombier static void	mimehost(Hlex*, char*);
5580ee5cbfSDavid du Colombier static void	mimeifrange(Hlex*, char*);
5680ee5cbfSDavid du Colombier static void	mimeignore(Hlex*, char*);
5780ee5cbfSDavid du Colombier static void	mimematch(Hlex*, char*);
5880ee5cbfSDavid du Colombier static void	mimemodified(Hlex*, char*);
5980ee5cbfSDavid du Colombier static void	mimenomatch(Hlex*, char*);
6080ee5cbfSDavid du Colombier static void	mimerange(Hlex*, char*);
6180ee5cbfSDavid du Colombier static void	mimetransenc(Hlex*, char*);
6280ee5cbfSDavid du Colombier static void	mimeunmodified(Hlex*, char*);
6380ee5cbfSDavid du Colombier 
6480ee5cbfSDavid du Colombier /*
6580ee5cbfSDavid du Colombier  * headers seen also include
6680ee5cbfSDavid du Colombier  * allow  cache-control chargeto
6780ee5cbfSDavid du Colombier  * content-encoding content-language content-location content-md5 content-range content-type
6880ee5cbfSDavid du Colombier  * date etag expires forwarded last-modified max-forwards pragma
6980ee5cbfSDavid du Colombier  * proxy-agent proxy-authorization proxy-connection
7080ee5cbfSDavid du Colombier  * ua-color ua-cpu ua-os ua-pixels
7180ee5cbfSDavid du Colombier  * upgrade via x-afs-tokens x-serial-number
7280ee5cbfSDavid du Colombier  */
7380ee5cbfSDavid du Colombier static MimeHead	mimehead[] =
7480ee5cbfSDavid du Colombier {
7580ee5cbfSDavid du Colombier 	{"accept",		mimeaccept},
7680ee5cbfSDavid du Colombier 	{"accept-charset",	mimeacceptchar},
7780ee5cbfSDavid du Colombier 	{"accept-encoding",	mimeacceptenc},
7880ee5cbfSDavid du Colombier 	{"accept-language",	mimeacceptlang},
7980ee5cbfSDavid du Colombier 	{"authorization",	mimeauthorization},
8080ee5cbfSDavid du Colombier 	{"connection",		mimeconnection},
8180ee5cbfSDavid du Colombier 	{"content-length",	mimecontlen},
822bef681aSDavid du Colombier 	{"cookie",		mimecookie},
8380ee5cbfSDavid du Colombier 	{"expect",		mimeexpect},
8480ee5cbfSDavid du Colombier 	{"fresh",		mimefresh},
8580ee5cbfSDavid du Colombier 	{"from",		mimefrom},
8680ee5cbfSDavid du Colombier 	{"host",		mimehost},
8780ee5cbfSDavid du Colombier 	{"if-match",		mimematch},
8880ee5cbfSDavid du Colombier 	{"if-modified-since",	mimemodified},
8980ee5cbfSDavid du Colombier 	{"if-none-match",	mimenomatch},
9080ee5cbfSDavid du Colombier 	{"if-range",		mimeifrange},
9180ee5cbfSDavid du Colombier 	{"if-unmodified-since",	mimeunmodified},
9280ee5cbfSDavid du Colombier 	{"range",		mimerange},
9380ee5cbfSDavid du Colombier 	{"transfer-encoding",	mimetransenc},
9480ee5cbfSDavid du Colombier 	{"user-agent",		mimeagent},
9580ee5cbfSDavid du Colombier };
9680ee5cbfSDavid du Colombier 
9780ee5cbfSDavid du Colombier char*		hmydomain;
9880ee5cbfSDavid du Colombier char*		hversion = "HTTP/1.1";
9980ee5cbfSDavid du Colombier 
10080ee5cbfSDavid du Colombier static	void	lexhead(Hlex*);
10180ee5cbfSDavid du Colombier static	void	parsejump(Hlex*, char*);
10280ee5cbfSDavid du Colombier static	int	getc(Hlex*);
10380ee5cbfSDavid du Colombier static	void	ungetc(Hlex*);
10480ee5cbfSDavid du Colombier static	int	wordcr(Hlex*);
10580ee5cbfSDavid du Colombier static	int	wordnl(Hlex*);
10680ee5cbfSDavid du Colombier static	void	word(Hlex*, char*);
10780ee5cbfSDavid du Colombier static	int	lex1(Hlex*, int);
10880ee5cbfSDavid du Colombier static	int	lex(Hlex*);
10980ee5cbfSDavid du Colombier static	int	lexbase64(Hlex*);
11080ee5cbfSDavid du Colombier static	ulong	digtoul(char *s, char **e);
11180ee5cbfSDavid du Colombier 
11280ee5cbfSDavid du Colombier /*
113*f9e1cf08SDavid du Colombier  * flush and clean up junk from a request
11480ee5cbfSDavid du Colombier  */
11580ee5cbfSDavid du Colombier void
11680ee5cbfSDavid du Colombier hreqcleanup(HConnect *c)
11780ee5cbfSDavid du Colombier {
11880ee5cbfSDavid du Colombier 	int i;
11980ee5cbfSDavid du Colombier 
12080ee5cbfSDavid du Colombier 	hxferenc(&c->hout, 0);
12180ee5cbfSDavid du Colombier 	memset(&c->req, 0, sizeof(c->req));
12280ee5cbfSDavid du Colombier 	memset(&c->head, 0, sizeof(c->head));
12380ee5cbfSDavid du Colombier 	c->hpos = c->header;
12480ee5cbfSDavid du Colombier 	c->hstop = c->header;
12580ee5cbfSDavid du Colombier 	binfree(&c->bin);
12680ee5cbfSDavid du Colombier 	for(i = 0; i < nelem(mimehead); i++){
12780ee5cbfSDavid du Colombier 		mimehead[i].seen = 0;
12880ee5cbfSDavid du Colombier 		mimehead[i].ignore = 0;
12980ee5cbfSDavid du Colombier 	}
13080ee5cbfSDavid du Colombier }
13180ee5cbfSDavid du Colombier 
13280ee5cbfSDavid du Colombier /*
13380ee5cbfSDavid du Colombier  * list of tokens
13480ee5cbfSDavid du Colombier  * if the client is HTTP/1.0,
13580ee5cbfSDavid du Colombier  * ignore headers which match one of the tokens.
13680ee5cbfSDavid du Colombier  * restarts parsing if necessary.
13780ee5cbfSDavid du Colombier  */
13880ee5cbfSDavid du Colombier static void
13980ee5cbfSDavid du Colombier mimeconnection(Hlex *h, char *)
14080ee5cbfSDavid du Colombier {
14180ee5cbfSDavid du Colombier 	char *u, *p;
14280ee5cbfSDavid du Colombier 	int reparse, i;
14380ee5cbfSDavid du Colombier 
14480ee5cbfSDavid du Colombier 	reparse = 0;
14580ee5cbfSDavid du Colombier 	for(;;){
14680ee5cbfSDavid du Colombier 		while(lex(h) != Word)
14780ee5cbfSDavid du Colombier 			if(h->tok != ',')
14880ee5cbfSDavid du Colombier 				goto breakout;
14980ee5cbfSDavid du Colombier 
15080ee5cbfSDavid du Colombier 		if(cistrcmp(h->wordval, "keep-alive") == 0)
15180ee5cbfSDavid du Colombier 			h->c->head.persist = 1;
15280ee5cbfSDavid du Colombier 		else if(cistrcmp(h->wordval, "close") == 0)
15380ee5cbfSDavid du Colombier 			h->c->head.closeit = 1;
15480ee5cbfSDavid du Colombier 		else if(!http11(h->c)){
15580ee5cbfSDavid du Colombier 			for(i = 0; i < nelem(mimehead); i++){
15680ee5cbfSDavid du Colombier 				if(cistrcmp(mimehead[i].name, h->wordval) == 0){
15780ee5cbfSDavid du Colombier 					reparse = mimehead[i].seen && !mimehead[i].ignore;
15880ee5cbfSDavid du Colombier 					mimehead[i].ignore = 1;
15980ee5cbfSDavid du Colombier 					if(cistrcmp(mimehead[i].name, "authorization") == 0){
16080ee5cbfSDavid du Colombier 						h->c->head.authuser = nil;
16180ee5cbfSDavid du Colombier 						h->c->head.authpass = nil;
16280ee5cbfSDavid du Colombier 					}
16380ee5cbfSDavid du Colombier 				}
16480ee5cbfSDavid du Colombier 			}
16580ee5cbfSDavid du Colombier 		}
16680ee5cbfSDavid du Colombier 
16780ee5cbfSDavid du Colombier 		if(lex(h) != ',')
16880ee5cbfSDavid du Colombier 			break;
16980ee5cbfSDavid du Colombier 	}
17080ee5cbfSDavid du Colombier 
17180ee5cbfSDavid du Colombier breakout:;
17280ee5cbfSDavid du Colombier 	/*
17380ee5cbfSDavid du Colombier 	 * if need to ignore headers we've already parsed,
17480ee5cbfSDavid du Colombier 	 * reset & start over.  need to save authorization
17580ee5cbfSDavid du Colombier 	 * info because it's written over when parsed.
17680ee5cbfSDavid du Colombier 	 */
17780ee5cbfSDavid du Colombier 	if(reparse){
17880ee5cbfSDavid du Colombier 		u = h->c->head.authuser;
17980ee5cbfSDavid du Colombier 		p = h->c->head.authpass;
18080ee5cbfSDavid du Colombier 		memset(&h->c->head, 0, sizeof(h->c->head));
18180ee5cbfSDavid du Colombier 		h->c->head.authuser = u;
18280ee5cbfSDavid du Colombier 		h->c->head.authpass = p;
18380ee5cbfSDavid du Colombier 
18480ee5cbfSDavid du Colombier 		h->c->hpos = h->hstart;
18580ee5cbfSDavid du Colombier 		longjmp(h->jmp, 1);
18680ee5cbfSDavid du Colombier 	}
18780ee5cbfSDavid du Colombier }
18880ee5cbfSDavid du Colombier 
18980ee5cbfSDavid du Colombier int
19080ee5cbfSDavid du Colombier hparseheaders(HConnect *c, int timeout)
19180ee5cbfSDavid du Colombier {
19280ee5cbfSDavid du Colombier 	Hlex h;
19380ee5cbfSDavid du Colombier 
19480ee5cbfSDavid du Colombier 	c->head.fresh_thresh = 0;
19580ee5cbfSDavid du Colombier 	c->head.fresh_have = 0;
19680ee5cbfSDavid du Colombier 	c->head.persist = 0;
19780ee5cbfSDavid du Colombier 	if(c->req.vermaj == 0){
19880ee5cbfSDavid du Colombier 		c->head.host = hmydomain;
19980ee5cbfSDavid du Colombier 		return 1;
20080ee5cbfSDavid du Colombier 	}
20180ee5cbfSDavid du Colombier 
20280ee5cbfSDavid du Colombier 	memset(&h, 0, sizeof(h));
20380ee5cbfSDavid du Colombier 	h.c = c;
204e059317eSDavid du Colombier 	if(timeout)
20580ee5cbfSDavid du Colombier 		alarm(timeout);
206e059317eSDavid du Colombier 	if(hgethead(c, 1) < 0)
2079a747e4fSDavid du Colombier 		return -1;
208e059317eSDavid du Colombier 	if(timeout)
20980ee5cbfSDavid du Colombier 		alarm(0);
21080ee5cbfSDavid du Colombier 	h.hstart = c->hpos;
21180ee5cbfSDavid du Colombier 
21280ee5cbfSDavid du Colombier 	if(setjmp(h.jmp) == -1)
21380ee5cbfSDavid du Colombier 		return -1;
21480ee5cbfSDavid du Colombier 
21580ee5cbfSDavid du Colombier 	h.eol = 0;
21680ee5cbfSDavid du Colombier 	h.eoh = 0;
21780ee5cbfSDavid du Colombier 	h.tok = '\n';
21880ee5cbfSDavid du Colombier 	while(lex(&h) != '\n'){
21980ee5cbfSDavid du Colombier 		if(h.tok == Word && lex(&h) == ':')
22080ee5cbfSDavid du Colombier 			parsejump(&h, hstrdup(c, h.wordval));
22180ee5cbfSDavid du Colombier 		while(h.tok != '\n')
22280ee5cbfSDavid du Colombier 			lex(&h);
22380ee5cbfSDavid du Colombier 		h.eol = h.eoh;
22480ee5cbfSDavid du Colombier 	}
22580ee5cbfSDavid du Colombier 
22680ee5cbfSDavid du Colombier 	if(http11(c)){
22780ee5cbfSDavid du Colombier 		/*
22880ee5cbfSDavid du Colombier 		 * according to the http/1.1 spec,
22980ee5cbfSDavid du Colombier 		 * these rules must be followed
23080ee5cbfSDavid du Colombier 		 */
23180ee5cbfSDavid du Colombier 		if(c->head.host == nil){
23280ee5cbfSDavid du Colombier 			hfail(c, HBadReq, nil);
23380ee5cbfSDavid du Colombier 			return -1;
23480ee5cbfSDavid du Colombier 		}
23580ee5cbfSDavid du Colombier 		if(c->req.urihost != nil)
23680ee5cbfSDavid du Colombier 			c->head.host = c->req.urihost;
23780ee5cbfSDavid du Colombier 		/*
23880ee5cbfSDavid du Colombier 		 * also need to check host is actually this one
23980ee5cbfSDavid du Colombier 		 */
24080ee5cbfSDavid du Colombier 	}else if(c->head.host == nil)
24180ee5cbfSDavid du Colombier 		c->head.host = hmydomain;
24280ee5cbfSDavid du Colombier 	return 1;
24380ee5cbfSDavid du Colombier }
24480ee5cbfSDavid du Colombier 
24580ee5cbfSDavid du Colombier /*
24680ee5cbfSDavid du Colombier  * mimeparams	: | mimeparams ";" mimepara
24780ee5cbfSDavid du Colombier  * mimeparam	: token "=" token | token "=" qstring
24880ee5cbfSDavid du Colombier  */
24980ee5cbfSDavid du Colombier static HSPairs*
25080ee5cbfSDavid du Colombier mimeparams(Hlex *h)
25180ee5cbfSDavid du Colombier {
25280ee5cbfSDavid du Colombier 	HSPairs *p;
25380ee5cbfSDavid du Colombier 	char *s;
25480ee5cbfSDavid du Colombier 
25580ee5cbfSDavid du Colombier 	p = nil;
25680ee5cbfSDavid du Colombier 	for(;;){
25780ee5cbfSDavid du Colombier 		if(lex(h) != Word)
25880ee5cbfSDavid du Colombier 			break;
25980ee5cbfSDavid du Colombier 		s = hstrdup(h->c, h->wordval);
26080ee5cbfSDavid du Colombier 		if(lex(h) != Word && h->tok != QString)
26180ee5cbfSDavid du Colombier 			break;
26280ee5cbfSDavid du Colombier 		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
26380ee5cbfSDavid du Colombier 	}
26480ee5cbfSDavid du Colombier 	return hrevspairs(p);
26580ee5cbfSDavid du Colombier }
26680ee5cbfSDavid du Colombier 
26780ee5cbfSDavid du Colombier /*
26880ee5cbfSDavid du Colombier  * mimehfields	: mimehfield | mimehfields commas mimehfield
26980ee5cbfSDavid du Colombier  * mimehfield	: token mimeparams
27080ee5cbfSDavid du Colombier  * commas	: "," | commas ","
27180ee5cbfSDavid du Colombier  */
27280ee5cbfSDavid du Colombier static HFields*
27380ee5cbfSDavid du Colombier mimehfields(Hlex *h)
27480ee5cbfSDavid du Colombier {
27580ee5cbfSDavid du Colombier 	HFields *f;
27680ee5cbfSDavid du Colombier 
27780ee5cbfSDavid du Colombier 	f = nil;
27880ee5cbfSDavid du Colombier 	for(;;){
27980ee5cbfSDavid du Colombier 		while(lex(h) != Word)
28080ee5cbfSDavid du Colombier 			if(h->tok != ',')
28180ee5cbfSDavid du Colombier 				goto breakout;
28280ee5cbfSDavid du Colombier 
28380ee5cbfSDavid du Colombier 		f = hmkhfields(h->c, hstrdup(h->c, h->wordval), nil, f);
28480ee5cbfSDavid du Colombier 
28580ee5cbfSDavid du Colombier 		if(lex(h) == ';')
28680ee5cbfSDavid du Colombier 			f->params = mimeparams(h);
28780ee5cbfSDavid du Colombier 		if(h->tok != ',')
28880ee5cbfSDavid du Colombier 			break;
28980ee5cbfSDavid du Colombier 	}
29080ee5cbfSDavid du Colombier breakout:;
29180ee5cbfSDavid du Colombier 	return hrevhfields(f);
29280ee5cbfSDavid du Colombier }
29380ee5cbfSDavid du Colombier 
29480ee5cbfSDavid du Colombier /*
29580ee5cbfSDavid du Colombier  * parse a list of acceptable types, encodings, languages, etc.
29680ee5cbfSDavid du Colombier  */
29780ee5cbfSDavid du Colombier static HContent*
29880ee5cbfSDavid du Colombier mimeok(Hlex *h, char *name, int multipart, HContent *head)
29980ee5cbfSDavid du Colombier {
30080ee5cbfSDavid du Colombier 	char *generic, *specific, *s;
30180ee5cbfSDavid du Colombier 	float v;
30280ee5cbfSDavid du Colombier 
30380ee5cbfSDavid du Colombier 	/*
30480ee5cbfSDavid du Colombier 	 * each type is separated by one or more commas
30580ee5cbfSDavid du Colombier 	 */
30680ee5cbfSDavid du Colombier 	while(lex(h) != Word)
30780ee5cbfSDavid du Colombier 		if(h->tok != ',')
30880ee5cbfSDavid du Colombier 			return head;
30980ee5cbfSDavid du Colombier 
31080ee5cbfSDavid du Colombier 	generic = hstrdup(h->c, h->wordval);
31180ee5cbfSDavid du Colombier 	lex(h);
31280ee5cbfSDavid du Colombier 	if(h->tok == '/' || multipart){
31380ee5cbfSDavid du Colombier 		/*
31480ee5cbfSDavid du Colombier 		 * at one time, IE5 improperly said '*' for single types
31580ee5cbfSDavid du Colombier 		 */
31680ee5cbfSDavid du Colombier 		if(h->tok != '/')
31780ee5cbfSDavid du Colombier 			return nil;
31880ee5cbfSDavid du Colombier 		if(lex(h) != Word)
31980ee5cbfSDavid du Colombier 			return head;
32080ee5cbfSDavid du Colombier 		specific = hstrdup(h->c, h->wordval);
32180ee5cbfSDavid du Colombier 		if(!multipart && strcmp(specific, "*") != 0)
32280ee5cbfSDavid du Colombier 			return head;
32380ee5cbfSDavid du Colombier 		lex(h);
32480ee5cbfSDavid du Colombier 	}else
32580ee5cbfSDavid du Colombier 		specific = nil;
32680ee5cbfSDavid du Colombier 	head = hmkcontent(h->c, generic, specific, head);
32780ee5cbfSDavid du Colombier 
32880ee5cbfSDavid du Colombier 	for(;;){
32980ee5cbfSDavid du Colombier 		switch(h->tok){
33080ee5cbfSDavid du Colombier 		case ';':
33180ee5cbfSDavid du Colombier 			/*
33280ee5cbfSDavid du Colombier 			 * should make a list of these params
33380ee5cbfSDavid du Colombier 			 * for accept, they fall into two classes:
33480ee5cbfSDavid du Colombier 			 *	up to a q=..., they modify the media type.
33580ee5cbfSDavid du Colombier 			 *	afterwards, they acceptance criteria
33680ee5cbfSDavid du Colombier 			 */
33780ee5cbfSDavid du Colombier 			if(lex(h) == Word){
33880ee5cbfSDavid du Colombier 				s = hstrdup(h->c, h->wordval);
33980ee5cbfSDavid du Colombier 				if(lex(h) != '=' || lex(h) != Word && h->tok != QString)
34080ee5cbfSDavid du Colombier 					return head;
34180ee5cbfSDavid du Colombier 				v = strtod(h->wordval, nil);
34280ee5cbfSDavid du Colombier 				if(strcmp(s, "q") == 0)
34380ee5cbfSDavid du Colombier 					head->q = v;
34480ee5cbfSDavid du Colombier 				else if(strcmp(s, "mxb") == 0)
34580ee5cbfSDavid du Colombier 					head->mxb = v;
34651711cb6SDavid du Colombier 				else{
34751711cb6SDavid du Colombier 					/* cope with accept: application/xhtml+xml; profile=http://www.wapforum.org/xhtml, */
34851711cb6SDavid du Colombier 					while(lex(h) == Word || (h->tok != ',' && h->eol == 0) )
34951711cb6SDavid du Colombier 						;
35051711cb6SDavid du Colombier 					return mimeok(h, name, multipart, head);
35151711cb6SDavid du Colombier 				}
35280ee5cbfSDavid du Colombier 			}
35380ee5cbfSDavid du Colombier 			break;
35480ee5cbfSDavid du Colombier 		case ',':
35580ee5cbfSDavid du Colombier 			return  mimeok(h, name, multipart, head);
35680ee5cbfSDavid du Colombier 		default:
35780ee5cbfSDavid du Colombier 			return head;
35880ee5cbfSDavid du Colombier 		}
35980ee5cbfSDavid du Colombier 		lex(h);
36080ee5cbfSDavid du Colombier 	}
36180ee5cbfSDavid du Colombier }
36280ee5cbfSDavid du Colombier 
36380ee5cbfSDavid du Colombier /*
36480ee5cbfSDavid du Colombier  * parse a list of entity tags
36580ee5cbfSDavid du Colombier  * 1#entity-tag
36680ee5cbfSDavid du Colombier  * entity-tag = [weak] opaque-tag
36780ee5cbfSDavid du Colombier  * weak = "W/"
36880ee5cbfSDavid du Colombier  * opaque-tag = quoted-string
36980ee5cbfSDavid du Colombier  */
37080ee5cbfSDavid du Colombier static HETag*
37180ee5cbfSDavid du Colombier mimeetag(Hlex *h, HETag *head)
37280ee5cbfSDavid du Colombier {
37380ee5cbfSDavid du Colombier 	HETag *e;
37480ee5cbfSDavid du Colombier 	int weak;
37580ee5cbfSDavid du Colombier 
37680ee5cbfSDavid du Colombier 	for(;;){
37780ee5cbfSDavid du Colombier 		while(lex(h) != Word && h->tok != QString)
37880ee5cbfSDavid du Colombier 			if(h->tok != ',')
37980ee5cbfSDavid du Colombier 				return head;
38080ee5cbfSDavid du Colombier 
38180ee5cbfSDavid du Colombier 		weak = 0;
38280ee5cbfSDavid du Colombier 		if(h->tok == Word && strcmp(h->wordval, "*") != 0){
38380ee5cbfSDavid du Colombier 			if(strcmp(h->wordval, "W") != 0)
38480ee5cbfSDavid du Colombier 				return head;
38580ee5cbfSDavid du Colombier 			if(lex(h) != '/' || lex(h) != QString)
38680ee5cbfSDavid du Colombier 				return head;
38780ee5cbfSDavid du Colombier 			weak = 1;
38880ee5cbfSDavid du Colombier 		}
38980ee5cbfSDavid du Colombier 
39080ee5cbfSDavid du Colombier 		e = halloc(h->c, sizeof(HETag));
39180ee5cbfSDavid du Colombier 		e->etag = hstrdup(h->c, h->wordval);
39280ee5cbfSDavid du Colombier 		e->weak = weak;
39380ee5cbfSDavid du Colombier 		e->next = head;
39480ee5cbfSDavid du Colombier 		head = e;
39580ee5cbfSDavid du Colombier 
39680ee5cbfSDavid du Colombier 		if(lex(h) != ',')
39780ee5cbfSDavid du Colombier 			return head;
39880ee5cbfSDavid du Colombier 	}
39980ee5cbfSDavid du Colombier }
40080ee5cbfSDavid du Colombier 
40180ee5cbfSDavid du Colombier /*
40280ee5cbfSDavid du Colombier  * ranges-specifier = byte-ranges-specifier
40380ee5cbfSDavid du Colombier  * byte-ranges-specifier = "bytes" "=" byte-range-set
40480ee5cbfSDavid du Colombier  * byte-range-set = 1#(byte-range-spec|suffix-byte-range-spec)
40580ee5cbfSDavid du Colombier  * byte-range-spec = byte-pos "-" [byte-pos]
40680ee5cbfSDavid du Colombier  * byte-pos = 1*DIGIT
40780ee5cbfSDavid du Colombier  * suffix-byte-range-spec = "-" suffix-length
40880ee5cbfSDavid du Colombier  * suffix-length = 1*DIGIT
40980ee5cbfSDavid du Colombier  *
41080ee5cbfSDavid du Colombier  * syntactically invalid range specifiers cause the
41180ee5cbfSDavid du Colombier  * entire header field to be ignored.
41280ee5cbfSDavid du Colombier  * it is syntactically incorrect for the second byte pos
41380ee5cbfSDavid du Colombier  * to be smaller than the first byte pos
41480ee5cbfSDavid du Colombier  */
41580ee5cbfSDavid du Colombier static HRange*
41680ee5cbfSDavid du Colombier mimeranges(Hlex *h, HRange *head)
41780ee5cbfSDavid du Colombier {
41880ee5cbfSDavid du Colombier 	HRange *r, *rh, *tail;
41980ee5cbfSDavid du Colombier 	char *w;
42080ee5cbfSDavid du Colombier 	ulong start, stop;
42180ee5cbfSDavid du Colombier 	int suf;
42280ee5cbfSDavid du Colombier 
42380ee5cbfSDavid du Colombier 	if(lex(h) != Word || strcmp(h->wordval, "bytes") != 0 || lex(h) != '=')
42480ee5cbfSDavid du Colombier 		return head;
42580ee5cbfSDavid du Colombier 
42680ee5cbfSDavid du Colombier 	rh = nil;
42780ee5cbfSDavid du Colombier 	tail = nil;
42880ee5cbfSDavid du Colombier 	for(;;){
42980ee5cbfSDavid du Colombier 		while(lex(h) != Word){
43080ee5cbfSDavid du Colombier 			if(h->tok != ','){
43180ee5cbfSDavid du Colombier 				if(h->tok == '\n')
43280ee5cbfSDavid du Colombier 					goto breakout;
43380ee5cbfSDavid du Colombier 				return head;
43480ee5cbfSDavid du Colombier 			}
43580ee5cbfSDavid du Colombier 		}
43680ee5cbfSDavid du Colombier 
43780ee5cbfSDavid du Colombier 		w = h->wordval;
43880ee5cbfSDavid du Colombier 		start = 0;
43980ee5cbfSDavid du Colombier 		suf = 1;
44080ee5cbfSDavid du Colombier 		if(w[0] != '-'){
44180ee5cbfSDavid du Colombier 			suf = 0;
44280ee5cbfSDavid du Colombier 			start = digtoul(w, &w);
44380ee5cbfSDavid du Colombier 			if(w[0] != '-')
44480ee5cbfSDavid du Colombier 				return head;
44580ee5cbfSDavid du Colombier 		}
44680ee5cbfSDavid du Colombier 		w++;
44780ee5cbfSDavid du Colombier 		stop = ~0UL;
44880ee5cbfSDavid du Colombier 		if(w[0] != '\0'){
44980ee5cbfSDavid du Colombier 			stop = digtoul(w, &w);
45080ee5cbfSDavid du Colombier 			if(w[0] != '\0')
45180ee5cbfSDavid du Colombier 				return head;
45280ee5cbfSDavid du Colombier 			if(!suf && stop < start)
45380ee5cbfSDavid du Colombier 				return head;
45480ee5cbfSDavid du Colombier 		}
45580ee5cbfSDavid du Colombier 
45680ee5cbfSDavid du Colombier 		r = halloc(h->c, sizeof(HRange));
45780ee5cbfSDavid du Colombier 		r->suffix = suf;
45880ee5cbfSDavid du Colombier 		r->start = start;
45980ee5cbfSDavid du Colombier 		r->stop = stop;
46080ee5cbfSDavid du Colombier 		r->next = nil;
46180ee5cbfSDavid du Colombier 		if(rh == nil)
46280ee5cbfSDavid du Colombier 			rh = r;
46380ee5cbfSDavid du Colombier 		else
46480ee5cbfSDavid du Colombier 			tail->next = r;
46580ee5cbfSDavid du Colombier 		tail = r;
46680ee5cbfSDavid du Colombier 
46780ee5cbfSDavid du Colombier 		if(lex(h) != ','){
46880ee5cbfSDavid du Colombier 			if(h->tok == '\n')
46980ee5cbfSDavid du Colombier 				break;
47080ee5cbfSDavid du Colombier 			return head;
47180ee5cbfSDavid du Colombier 		}
47280ee5cbfSDavid du Colombier 	}
47380ee5cbfSDavid du Colombier breakout:;
47480ee5cbfSDavid du Colombier 
47580ee5cbfSDavid du Colombier 	if(head == nil)
47680ee5cbfSDavid du Colombier 		return rh;
47780ee5cbfSDavid du Colombier 
47880ee5cbfSDavid du Colombier 	for(tail = head; tail->next != nil; tail = tail->next)
47980ee5cbfSDavid du Colombier 		;
48080ee5cbfSDavid du Colombier 	tail->next = rh;
48180ee5cbfSDavid du Colombier 	return head;
48280ee5cbfSDavid du Colombier }
48380ee5cbfSDavid du Colombier 
48480ee5cbfSDavid du Colombier static void
48580ee5cbfSDavid du Colombier mimeaccept(Hlex *h, char *name)
48680ee5cbfSDavid du Colombier {
48780ee5cbfSDavid du Colombier 	h->c->head.oktype = mimeok(h, name, 1, h->c->head.oktype);
48880ee5cbfSDavid du Colombier }
48980ee5cbfSDavid du Colombier 
49080ee5cbfSDavid du Colombier static void
49180ee5cbfSDavid du Colombier mimeacceptchar(Hlex *h, char *name)
49280ee5cbfSDavid du Colombier {
49380ee5cbfSDavid du Colombier 	h->c->head.okchar = mimeok(h, name, 0, h->c->head.okchar);
49480ee5cbfSDavid du Colombier }
49580ee5cbfSDavid du Colombier 
49680ee5cbfSDavid du Colombier static void
49780ee5cbfSDavid du Colombier mimeacceptenc(Hlex *h, char *name)
49880ee5cbfSDavid du Colombier {
49980ee5cbfSDavid du Colombier 	h->c->head.okencode = mimeok(h, name, 0, h->c->head.okencode);
50080ee5cbfSDavid du Colombier }
50180ee5cbfSDavid du Colombier 
50280ee5cbfSDavid du Colombier static void
50380ee5cbfSDavid du Colombier mimeacceptlang(Hlex *h, char *name)
50480ee5cbfSDavid du Colombier {
50580ee5cbfSDavid du Colombier 	h->c->head.oklang = mimeok(h, name, 0, h->c->head.oklang);
50680ee5cbfSDavid du Colombier }
50780ee5cbfSDavid du Colombier 
50880ee5cbfSDavid du Colombier static void
50980ee5cbfSDavid du Colombier mimemodified(Hlex *h, char *)
51080ee5cbfSDavid du Colombier {
51180ee5cbfSDavid du Colombier 	lexhead(h);
51280ee5cbfSDavid du Colombier 	h->c->head.ifmodsince = hdate2sec(h->wordval);
51380ee5cbfSDavid du Colombier }
51480ee5cbfSDavid du Colombier 
51580ee5cbfSDavid du Colombier static void
51680ee5cbfSDavid du Colombier mimeunmodified(Hlex *h, char *)
51780ee5cbfSDavid du Colombier {
51880ee5cbfSDavid du Colombier 	lexhead(h);
51980ee5cbfSDavid du Colombier 	h->c->head.ifunmodsince = hdate2sec(h->wordval);
52080ee5cbfSDavid du Colombier }
52180ee5cbfSDavid du Colombier 
52280ee5cbfSDavid du Colombier static void
52380ee5cbfSDavid du Colombier mimematch(Hlex *h, char *)
52480ee5cbfSDavid du Colombier {
52580ee5cbfSDavid du Colombier 	h->c->head.ifmatch = mimeetag(h, h->c->head.ifmatch);
52680ee5cbfSDavid du Colombier }
52780ee5cbfSDavid du Colombier 
52880ee5cbfSDavid du Colombier static void
52980ee5cbfSDavid du Colombier mimenomatch(Hlex *h, char *)
53080ee5cbfSDavid du Colombier {
53180ee5cbfSDavid du Colombier 	h->c->head.ifnomatch = mimeetag(h, h->c->head.ifnomatch);
53280ee5cbfSDavid du Colombier }
53380ee5cbfSDavid du Colombier 
53480ee5cbfSDavid du Colombier /*
53580ee5cbfSDavid du Colombier  * argument is either etag or date
53680ee5cbfSDavid du Colombier  */
53780ee5cbfSDavid du Colombier static void
53880ee5cbfSDavid du Colombier mimeifrange(Hlex *h, char *)
53980ee5cbfSDavid du Colombier {
54080ee5cbfSDavid du Colombier 	int c, d, et;
54180ee5cbfSDavid du Colombier 
54280ee5cbfSDavid du Colombier 	et = 0;
54380ee5cbfSDavid du Colombier 	c = getc(h);
54480ee5cbfSDavid du Colombier 	while(c == ' ' || c == '\t')
54580ee5cbfSDavid du Colombier 		c = getc(h);
54680ee5cbfSDavid du Colombier 	if(c == '"')
54780ee5cbfSDavid du Colombier 		et = 1;
54880ee5cbfSDavid du Colombier 	else if(c == 'W'){
54980ee5cbfSDavid du Colombier 		d = getc(h);
55080ee5cbfSDavid du Colombier 		if(d == '/')
55180ee5cbfSDavid du Colombier 			et = 1;
55280ee5cbfSDavid du Colombier 		ungetc(h);
55380ee5cbfSDavid du Colombier 	}
55480ee5cbfSDavid du Colombier 	ungetc(h);
55580ee5cbfSDavid du Colombier 	if(et){
55680ee5cbfSDavid du Colombier 		h->c->head.ifrangeetag = mimeetag(h, h->c->head.ifrangeetag);
55780ee5cbfSDavid du Colombier 	}else{
55880ee5cbfSDavid du Colombier 		lexhead(h);
55980ee5cbfSDavid du Colombier 		h->c->head.ifrangedate = hdate2sec(h->wordval);
56080ee5cbfSDavid du Colombier 	}
56180ee5cbfSDavid du Colombier }
56280ee5cbfSDavid du Colombier 
56380ee5cbfSDavid du Colombier static void
56480ee5cbfSDavid du Colombier mimerange(Hlex *h, char *)
56580ee5cbfSDavid du Colombier {
56680ee5cbfSDavid du Colombier 	h->c->head.range = mimeranges(h, h->c->head.range);
56780ee5cbfSDavid du Colombier }
56880ee5cbfSDavid du Colombier 
56980ee5cbfSDavid du Colombier /*
57080ee5cbfSDavid du Colombier  * note: netscape and ie through versions 4.7 and 4
57180ee5cbfSDavid du Colombier  * support only basic authorization, so that is all that is supported here
57280ee5cbfSDavid du Colombier  *
57380ee5cbfSDavid du Colombier  * "Authorization" ":" "Basic" base64-user-pass
57480ee5cbfSDavid du Colombier  * where base64-user-pass is the base64 encoding of
57580ee5cbfSDavid du Colombier  * username ":" password
57680ee5cbfSDavid du Colombier  */
57780ee5cbfSDavid du Colombier static void
57880ee5cbfSDavid du Colombier mimeauthorization(Hlex *h, char *)
57980ee5cbfSDavid du Colombier {
58080ee5cbfSDavid du Colombier 	char *up, *p;
58180ee5cbfSDavid du Colombier 	int n;
58280ee5cbfSDavid du Colombier 
58380ee5cbfSDavid du Colombier 	if(lex(h) != Word || cistrcmp(h->wordval, "basic") != 0)
58480ee5cbfSDavid du Colombier 		return;
58580ee5cbfSDavid du Colombier 
58680ee5cbfSDavid du Colombier 	n = lexbase64(h);
58780ee5cbfSDavid du Colombier 	if(!n)
58880ee5cbfSDavid du Colombier 		return;
58980ee5cbfSDavid du Colombier 
59080ee5cbfSDavid du Colombier 	/*
59180ee5cbfSDavid du Colombier 	 * wipe out source for password, so it won't be logged.
59280ee5cbfSDavid du Colombier 	 * it is replaced by a single =,
59380ee5cbfSDavid du Colombier 	 * which is valid base64, but not ok for an auth reponse.
59480ee5cbfSDavid du Colombier 	 * therefore future parses of the header field will not overwrite
59580ee5cbfSDavid du Colombier 	 * authuser and authpass.
59680ee5cbfSDavid du Colombier 	 */
59780ee5cbfSDavid du Colombier 	memmove(h->c->hpos - (n - 1), h->c->hpos, h->c->hstop - h->c->hpos);
59880ee5cbfSDavid du Colombier 	h->c->hstop -= n - 1;
59980ee5cbfSDavid du Colombier 	*h->c->hstop = '\0';
60080ee5cbfSDavid du Colombier 	h->c->hpos -= n - 1;
60180ee5cbfSDavid du Colombier 	h->c->hpos[-1] = '=';
60280ee5cbfSDavid du Colombier 
60380ee5cbfSDavid du Colombier 	up = halloc(h->c, n + 1);
60480ee5cbfSDavid du Colombier 	n = dec64((uchar*)up, n, h->wordval, n);
60580ee5cbfSDavid du Colombier 	up[n] = '\0';
60680ee5cbfSDavid du Colombier 	p = strchr(up, ':');
60780ee5cbfSDavid du Colombier 	if(p != nil){
60880ee5cbfSDavid du Colombier 		*p++ = '\0';
60980ee5cbfSDavid du Colombier 		h->c->head.authuser = hstrdup(h->c, up);
61080ee5cbfSDavid du Colombier 		h->c->head.authpass = hstrdup(h->c, p);
61180ee5cbfSDavid du Colombier 	}
61280ee5cbfSDavid du Colombier }
61380ee5cbfSDavid du Colombier 
61480ee5cbfSDavid du Colombier static void
61580ee5cbfSDavid du Colombier mimeagent(Hlex *h, char *)
61680ee5cbfSDavid du Colombier {
61780ee5cbfSDavid du Colombier 	lexhead(h);
61880ee5cbfSDavid du Colombier 	h->c->head.client = hstrdup(h->c, h->wordval);
61980ee5cbfSDavid du Colombier }
62080ee5cbfSDavid du Colombier 
62180ee5cbfSDavid du Colombier static void
62280ee5cbfSDavid du Colombier mimefrom(Hlex *h, char *)
62380ee5cbfSDavid du Colombier {
62480ee5cbfSDavid du Colombier 	lexhead(h);
62580ee5cbfSDavid du Colombier }
62680ee5cbfSDavid du Colombier 
62780ee5cbfSDavid du Colombier static void
62880ee5cbfSDavid du Colombier mimehost(Hlex *h, char *)
62980ee5cbfSDavid du Colombier {
63080ee5cbfSDavid du Colombier 	char *hd;
63180ee5cbfSDavid du Colombier 
63280ee5cbfSDavid du Colombier 	lexhead(h);
63380ee5cbfSDavid du Colombier 	for(hd = h->wordval; *hd == ' ' || *hd == '\t'; hd++)
63480ee5cbfSDavid du Colombier 		;
63580ee5cbfSDavid du Colombier 	h->c->head.host = hlower(hstrdup(h->c, hd));
63680ee5cbfSDavid du Colombier }
63780ee5cbfSDavid du Colombier 
63880ee5cbfSDavid du Colombier /*
63980ee5cbfSDavid du Colombier  * if present, implies that a message body follows the headers
64080ee5cbfSDavid du Colombier  * "content-length" ":" digits
64180ee5cbfSDavid du Colombier  */
64280ee5cbfSDavid du Colombier static void
64380ee5cbfSDavid du Colombier mimecontlen(Hlex *h, char *)
64480ee5cbfSDavid du Colombier {
64580ee5cbfSDavid du Colombier 	char *e;
64680ee5cbfSDavid du Colombier 	ulong v;
64780ee5cbfSDavid du Colombier 
64880ee5cbfSDavid du Colombier 	if(lex(h) != Word)
64980ee5cbfSDavid du Colombier 		return;
65080ee5cbfSDavid du Colombier 	e = h->wordval;
65180ee5cbfSDavid du Colombier 	v = digtoul(e, &e);
65280ee5cbfSDavid du Colombier 	if(v == ~0UL || *e != '\0')
65380ee5cbfSDavid du Colombier 		return;
65480ee5cbfSDavid du Colombier 	h->c->head.contlen = v;
65580ee5cbfSDavid du Colombier }
65680ee5cbfSDavid du Colombier 
65780ee5cbfSDavid du Colombier /*
65880ee5cbfSDavid du Colombier  * mimexpect	: "expect" ":" expects
65980ee5cbfSDavid du Colombier  * expects	: | expects "," expect
66080ee5cbfSDavid du Colombier  * expect	: "100-continue" | token | token "=" token expectparams | token "=" qstring expectparams
66180ee5cbfSDavid du Colombier  * expectparams	: ";" token | ";" token "=" token | token "=" qstring
66280ee5cbfSDavid du Colombier  * for now, we merely parse "100-continue" or anything else.
66380ee5cbfSDavid du Colombier  */
66480ee5cbfSDavid du Colombier static void
66580ee5cbfSDavid du Colombier mimeexpect(Hlex *h, char *)
66680ee5cbfSDavid du Colombier {
66780ee5cbfSDavid du Colombier 	if(lex(h) != Word || cistrcmp(h->wordval, "100-continue") != 0 || lex(h) != '\n')
66880ee5cbfSDavid du Colombier 		h->c->head.expectother = 1;
66980ee5cbfSDavid du Colombier 	h->c->head.expectcont = 1;
67080ee5cbfSDavid du Colombier }
67180ee5cbfSDavid du Colombier 
67280ee5cbfSDavid du Colombier static void
67380ee5cbfSDavid du Colombier mimetransenc(Hlex *h, char *)
67480ee5cbfSDavid du Colombier {
67580ee5cbfSDavid du Colombier 	h->c->head.transenc = mimehfields(h);
67680ee5cbfSDavid du Colombier }
67780ee5cbfSDavid du Colombier 
67880ee5cbfSDavid du Colombier static void
6792bef681aSDavid du Colombier mimecookie(Hlex *h, char *)
6802bef681aSDavid du Colombier {
6812bef681aSDavid du Colombier 	char *s;
6822bef681aSDavid du Colombier 	HSPairs *p;
6832bef681aSDavid du Colombier 
6842bef681aSDavid du Colombier 	p = nil;
6852bef681aSDavid du Colombier 	for(;;){
6862bef681aSDavid du Colombier 		while(lex(h) != Word)
6872bef681aSDavid du Colombier 			if(h->tok != ';' && h->tok != ',')
6882bef681aSDavid du Colombier 				goto breakout;
6892bef681aSDavid du Colombier 		s = hstrdup(h->c, h->wordval);
6902bef681aSDavid du Colombier 		while (lex(h) != Word && h->tok != QString)
6912bef681aSDavid du Colombier 			if (h->tok != '=')
6922bef681aSDavid du Colombier 				goto breakout;
6932bef681aSDavid du Colombier 		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
6942bef681aSDavid du Colombier 	}
6952bef681aSDavid du Colombier breakout:
6962bef681aSDavid du Colombier 	h->c->head.cookie = hrevspairs(p);
6972bef681aSDavid du Colombier }
6982bef681aSDavid du Colombier 
6992bef681aSDavid du Colombier static void
70080ee5cbfSDavid du Colombier mimefresh(Hlex *h, char *)
70180ee5cbfSDavid du Colombier {
70280ee5cbfSDavid du Colombier 	char *s;
70380ee5cbfSDavid du Colombier 
70480ee5cbfSDavid du Colombier 	lexhead(h);
70580ee5cbfSDavid du Colombier 	for(s = h->wordval; *s && (*s==' ' || *s=='\t'); s++)
70680ee5cbfSDavid du Colombier 		;
70780ee5cbfSDavid du Colombier 	if(strncmp(s, "pathstat/", 9) == 0)
70880ee5cbfSDavid du Colombier 		h->c->head.fresh_thresh = atoi(s+9);
70980ee5cbfSDavid du Colombier 	else if(strncmp(s, "have/", 5) == 0)
71080ee5cbfSDavid du Colombier 		h->c->head.fresh_have = atoi(s+5);
71180ee5cbfSDavid du Colombier }
71280ee5cbfSDavid du Colombier 
71380ee5cbfSDavid du Colombier static void
71480ee5cbfSDavid du Colombier mimeignore(Hlex *h, char *)
71580ee5cbfSDavid du Colombier {
71680ee5cbfSDavid du Colombier 	lexhead(h);
71780ee5cbfSDavid du Colombier }
71880ee5cbfSDavid du Colombier 
71980ee5cbfSDavid du Colombier static void
72080ee5cbfSDavid du Colombier parsejump(Hlex *h, char *k)
72180ee5cbfSDavid du Colombier {
72280ee5cbfSDavid du Colombier 	int l, r, m;
72380ee5cbfSDavid du Colombier 
72480ee5cbfSDavid du Colombier 	l = 1;
72580ee5cbfSDavid du Colombier 	r = nelem(mimehead) - 1;
72680ee5cbfSDavid du Colombier 	while(l <= r){
72780ee5cbfSDavid du Colombier 		m = (r + l) >> 1;
72880ee5cbfSDavid du Colombier 		if(cistrcmp(mimehead[m].name, k) <= 0)
72980ee5cbfSDavid du Colombier 			l = m + 1;
73080ee5cbfSDavid du Colombier 		else
73180ee5cbfSDavid du Colombier 			r = m - 1;
73280ee5cbfSDavid du Colombier 	}
73380ee5cbfSDavid du Colombier 	m = l - 1;
73480ee5cbfSDavid du Colombier 	if(cistrcmp(mimehead[m].name, k) == 0 && !mimehead[m].ignore){
73580ee5cbfSDavid du Colombier 		mimehead[m].seen = 1;
73680ee5cbfSDavid du Colombier 		(*mimehead[m].parse)(h, k);
73780ee5cbfSDavid du Colombier 	}else
73880ee5cbfSDavid du Colombier 		mimeignore(h, k);
73980ee5cbfSDavid du Colombier }
74080ee5cbfSDavid du Colombier 
74180ee5cbfSDavid du Colombier static int
74280ee5cbfSDavid du Colombier lex(Hlex *h)
74380ee5cbfSDavid du Colombier {
74480ee5cbfSDavid du Colombier 	return h->tok = lex1(h, 0);
74580ee5cbfSDavid du Colombier }
74680ee5cbfSDavid du Colombier 
74780ee5cbfSDavid du Colombier static int
74880ee5cbfSDavid du Colombier lexbase64(Hlex *h)
74980ee5cbfSDavid du Colombier {
75080ee5cbfSDavid du Colombier 	int c, n;
75180ee5cbfSDavid du Colombier 
75280ee5cbfSDavid du Colombier 	n = 0;
75380ee5cbfSDavid du Colombier 	lex1(h, 1);
75480ee5cbfSDavid du Colombier 
75580ee5cbfSDavid du Colombier 	while((c = getc(h)) >= 0){
756*f9e1cf08SDavid du Colombier 		if(!isalnum(c) && c != '+' && c != '/'){
75780ee5cbfSDavid du Colombier 			ungetc(h);
75880ee5cbfSDavid du Colombier 			break;
75980ee5cbfSDavid du Colombier 		}
76080ee5cbfSDavid du Colombier 		if(n < HMaxWord-1)
76180ee5cbfSDavid du Colombier 			h->wordval[n++] = c;
76280ee5cbfSDavid du Colombier 	}
76380ee5cbfSDavid du Colombier 	h->wordval[n] = '\0';
76480ee5cbfSDavid du Colombier 	return n;
76580ee5cbfSDavid du Colombier }
76680ee5cbfSDavid du Colombier 
76780ee5cbfSDavid du Colombier /*
76880ee5cbfSDavid du Colombier  * rfc 822/rfc 1521 lexical analyzer
76980ee5cbfSDavid du Colombier  */
77080ee5cbfSDavid du Colombier static int
77180ee5cbfSDavid du Colombier lex1(Hlex *h, int skipwhite)
77280ee5cbfSDavid du Colombier {
77380ee5cbfSDavid du Colombier 	int level, c;
77480ee5cbfSDavid du Colombier 
77580ee5cbfSDavid du Colombier 	if(h->eol)
77680ee5cbfSDavid du Colombier 		return '\n';
77780ee5cbfSDavid du Colombier 
77880ee5cbfSDavid du Colombier top:
77980ee5cbfSDavid du Colombier 	c = getc(h);
78080ee5cbfSDavid du Colombier 	switch(c){
78180ee5cbfSDavid du Colombier 	case '(':
78280ee5cbfSDavid du Colombier 		level = 1;
78380ee5cbfSDavid du Colombier 		while((c = getc(h)) >= 0){
78480ee5cbfSDavid du Colombier 			if(c == '\\'){
78580ee5cbfSDavid du Colombier 				c = getc(h);
78680ee5cbfSDavid du Colombier 				if(c < 0)
78780ee5cbfSDavid du Colombier 					return '\n';
78880ee5cbfSDavid du Colombier 				continue;
78980ee5cbfSDavid du Colombier 			}
79080ee5cbfSDavid du Colombier 			if(c == '(')
79180ee5cbfSDavid du Colombier 				level++;
79280ee5cbfSDavid du Colombier 			else if(c == ')' && --level == 0)
79380ee5cbfSDavid du Colombier 				break;
79480ee5cbfSDavid du Colombier 			else if(c == '\n'){
79580ee5cbfSDavid du Colombier 				c = getc(h);
79680ee5cbfSDavid du Colombier 				if(c < 0)
79780ee5cbfSDavid du Colombier 					return '\n';
79880ee5cbfSDavid du Colombier 				if(c == ')' && --level == 0)
79980ee5cbfSDavid du Colombier 					break;
80080ee5cbfSDavid du Colombier 				if(c != ' ' && c != '\t'){
80180ee5cbfSDavid du Colombier 					ungetc(h);
80280ee5cbfSDavid du Colombier 					return '\n';
80380ee5cbfSDavid du Colombier 				}
80480ee5cbfSDavid du Colombier 			}
80580ee5cbfSDavid du Colombier 		}
80680ee5cbfSDavid du Colombier 		goto top;
80780ee5cbfSDavid du Colombier 
80880ee5cbfSDavid du Colombier 	case ' ': case '\t':
80980ee5cbfSDavid du Colombier 		goto top;
81080ee5cbfSDavid du Colombier 
81180ee5cbfSDavid du Colombier 	case '\r':
81280ee5cbfSDavid du Colombier 		c = getc(h);
81380ee5cbfSDavid du Colombier 		if(c != '\n'){
81480ee5cbfSDavid du Colombier 			ungetc(h);
81580ee5cbfSDavid du Colombier 			goto top;
81680ee5cbfSDavid du Colombier 		}
81780ee5cbfSDavid du Colombier 
81880ee5cbfSDavid du Colombier 	case '\n':
81980ee5cbfSDavid du Colombier 		if(h->tok == '\n'){
82080ee5cbfSDavid du Colombier 			h->eol = 1;
82180ee5cbfSDavid du Colombier 			h->eoh = 1;
82280ee5cbfSDavid du Colombier 			return '\n';
82380ee5cbfSDavid du Colombier 		}
82480ee5cbfSDavid du Colombier 		c = getc(h);
82580ee5cbfSDavid du Colombier 		if(c < 0){
82680ee5cbfSDavid du Colombier 			h->eol = 1;
82780ee5cbfSDavid du Colombier 			return '\n';
82880ee5cbfSDavid du Colombier 		}
82980ee5cbfSDavid du Colombier 		if(c != ' ' && c != '\t'){
83080ee5cbfSDavid du Colombier 			ungetc(h);
83180ee5cbfSDavid du Colombier 			h->eol = 1;
83280ee5cbfSDavid du Colombier 			return '\n';
83380ee5cbfSDavid du Colombier 		}
83480ee5cbfSDavid du Colombier 		goto top;
83580ee5cbfSDavid du Colombier 
83680ee5cbfSDavid du Colombier 	case ')':
83780ee5cbfSDavid du Colombier 	case '<': case '>':
83880ee5cbfSDavid du Colombier 	case '[': case ']':
83980ee5cbfSDavid du Colombier 	case '@': case '/':
84080ee5cbfSDavid du Colombier 	case ',': case ';': case ':': case '?': case '=':
84180ee5cbfSDavid du Colombier 		if(skipwhite){
84280ee5cbfSDavid du Colombier 			ungetc(h);
84380ee5cbfSDavid du Colombier 			return c;
84480ee5cbfSDavid du Colombier 		}
84580ee5cbfSDavid du Colombier 		return c;
84680ee5cbfSDavid du Colombier 
84780ee5cbfSDavid du Colombier 	case '"':
84880ee5cbfSDavid du Colombier 		if(skipwhite){
84980ee5cbfSDavid du Colombier 			ungetc(h);
85080ee5cbfSDavid du Colombier 			return c;
85180ee5cbfSDavid du Colombier 		}
85280ee5cbfSDavid du Colombier 		word(h, "\"");
85380ee5cbfSDavid du Colombier 		getc(h);		/* skip the closing quote */
85480ee5cbfSDavid du Colombier 		return QString;
85580ee5cbfSDavid du Colombier 
85680ee5cbfSDavid du Colombier 	default:
85780ee5cbfSDavid du Colombier 		ungetc(h);
85880ee5cbfSDavid du Colombier 		if(skipwhite)
85980ee5cbfSDavid du Colombier 			return c;
86080ee5cbfSDavid du Colombier 		word(h, "\"(){}<>@,;:/[]?=\r\n \t");
86180ee5cbfSDavid du Colombier 		if(h->wordval[0] == '\0'){
86280ee5cbfSDavid du Colombier 			h->c->head.closeit = 1;
86380ee5cbfSDavid du Colombier 			hfail(h->c, HSyntax);
86480ee5cbfSDavid du Colombier 			longjmp(h->jmp, -1);
86580ee5cbfSDavid du Colombier 		}
86680ee5cbfSDavid du Colombier 		return Word;
86780ee5cbfSDavid du Colombier 	}
868b85a8364SDavid du Colombier 	/* not reached */
86980ee5cbfSDavid du Colombier }
87080ee5cbfSDavid du Colombier 
87180ee5cbfSDavid du Colombier /*
87280ee5cbfSDavid du Colombier  * return the rest of an rfc 822, including \n
87380ee5cbfSDavid du Colombier  * do not map to lower case
87480ee5cbfSDavid du Colombier  */
87580ee5cbfSDavid du Colombier static void
87680ee5cbfSDavid du Colombier lexhead(Hlex *h)
87780ee5cbfSDavid du Colombier {
87880ee5cbfSDavid du Colombier 	int c, n;
87980ee5cbfSDavid du Colombier 
88080ee5cbfSDavid du Colombier 	n = 0;
88180ee5cbfSDavid du Colombier 	while((c = getc(h)) >= 0){
88280ee5cbfSDavid du Colombier 		if(c == '\r')
88380ee5cbfSDavid du Colombier 			c = wordcr(h);
88480ee5cbfSDavid du Colombier 		else if(c == '\n')
88580ee5cbfSDavid du Colombier 			c = wordnl(h);
88680ee5cbfSDavid du Colombier 		if(c == '\n')
88780ee5cbfSDavid du Colombier 			break;
88880ee5cbfSDavid du Colombier 		if(c == '\\'){
88980ee5cbfSDavid du Colombier 			c = getc(h);
89080ee5cbfSDavid du Colombier 			if(c < 0)
89180ee5cbfSDavid du Colombier 				break;
89280ee5cbfSDavid du Colombier 		}
89380ee5cbfSDavid du Colombier 
89480ee5cbfSDavid du Colombier 		if(n < HMaxWord-1)
89580ee5cbfSDavid du Colombier 			h->wordval[n++] = c;
89680ee5cbfSDavid du Colombier 	}
89780ee5cbfSDavid du Colombier 	h->tok = '\n';
89880ee5cbfSDavid du Colombier 	h->eol = 1;
89980ee5cbfSDavid du Colombier 	h->wordval[n] = '\0';
90080ee5cbfSDavid du Colombier }
90180ee5cbfSDavid du Colombier 
90280ee5cbfSDavid du Colombier static void
90380ee5cbfSDavid du Colombier word(Hlex *h, char *stop)
90480ee5cbfSDavid du Colombier {
90580ee5cbfSDavid du Colombier 	int c, n;
90680ee5cbfSDavid du Colombier 
90780ee5cbfSDavid du Colombier 	n = 0;
90880ee5cbfSDavid du Colombier 	while((c = getc(h)) >= 0){
90980ee5cbfSDavid du Colombier 		if(c == '\r')
91080ee5cbfSDavid du Colombier 			c = wordcr(h);
91180ee5cbfSDavid du Colombier 		else if(c == '\n')
91280ee5cbfSDavid du Colombier 			c = wordnl(h);
91380ee5cbfSDavid du Colombier 		if(c == '\\'){
91480ee5cbfSDavid du Colombier 			c = getc(h);
91580ee5cbfSDavid du Colombier 			if(c < 0)
91680ee5cbfSDavid du Colombier 				break;
91780ee5cbfSDavid du Colombier 		}else if(c < 32 || strchr(stop, c) != nil){
91880ee5cbfSDavid du Colombier 			ungetc(h);
91980ee5cbfSDavid du Colombier 			break;
92080ee5cbfSDavid du Colombier 		}
92180ee5cbfSDavid du Colombier 
92280ee5cbfSDavid du Colombier 		if(n < HMaxWord-1)
92380ee5cbfSDavid du Colombier 			h->wordval[n++] = c;
92480ee5cbfSDavid du Colombier 	}
92580ee5cbfSDavid du Colombier 	h->wordval[n] = '\0';
92680ee5cbfSDavid du Colombier }
92780ee5cbfSDavid du Colombier 
92880ee5cbfSDavid du Colombier static int
92980ee5cbfSDavid du Colombier wordcr(Hlex *h)
93080ee5cbfSDavid du Colombier {
93180ee5cbfSDavid du Colombier 	int c;
93280ee5cbfSDavid du Colombier 
93380ee5cbfSDavid du Colombier 	c = getc(h);
93480ee5cbfSDavid du Colombier 	if(c == '\n')
93580ee5cbfSDavid du Colombier 		return wordnl(h);
93680ee5cbfSDavid du Colombier 	ungetc(h);
93780ee5cbfSDavid du Colombier 	return ' ';
93880ee5cbfSDavid du Colombier }
93980ee5cbfSDavid du Colombier 
94080ee5cbfSDavid du Colombier static int
94180ee5cbfSDavid du Colombier wordnl(Hlex *h)
94280ee5cbfSDavid du Colombier {
94380ee5cbfSDavid du Colombier 	int c;
94480ee5cbfSDavid du Colombier 
94580ee5cbfSDavid du Colombier 	c = getc(h);
94680ee5cbfSDavid du Colombier 	if(c == ' ' || c == '\t')
94780ee5cbfSDavid du Colombier 		return c;
94880ee5cbfSDavid du Colombier 	ungetc(h);
94980ee5cbfSDavid du Colombier 
95080ee5cbfSDavid du Colombier 	return '\n';
95180ee5cbfSDavid du Colombier }
95280ee5cbfSDavid du Colombier 
95380ee5cbfSDavid du Colombier static int
95480ee5cbfSDavid du Colombier getc(Hlex *h)
95580ee5cbfSDavid du Colombier {
95680ee5cbfSDavid du Colombier 	if(h->eoh)
95780ee5cbfSDavid du Colombier 		return -1;
95880ee5cbfSDavid du Colombier 	if(h->c->hpos < h->c->hstop)
95980ee5cbfSDavid du Colombier 		return *h->c->hpos++;
96080ee5cbfSDavid du Colombier 	h->eoh = 1;
96180ee5cbfSDavid du Colombier 	h->eol = 1;
96280ee5cbfSDavid du Colombier 	return -1;
96380ee5cbfSDavid du Colombier }
96480ee5cbfSDavid du Colombier 
96580ee5cbfSDavid du Colombier static void
96680ee5cbfSDavid du Colombier ungetc(Hlex *h)
96780ee5cbfSDavid du Colombier {
96880ee5cbfSDavid du Colombier 	if(h->eoh)
96980ee5cbfSDavid du Colombier 		return;
97080ee5cbfSDavid du Colombier 	h->c->hpos--;
97180ee5cbfSDavid du Colombier }
97280ee5cbfSDavid du Colombier 
97380ee5cbfSDavid du Colombier static ulong
97480ee5cbfSDavid du Colombier digtoul(char *s, char **e)
97580ee5cbfSDavid du Colombier {
97680ee5cbfSDavid du Colombier 	ulong v;
97780ee5cbfSDavid du Colombier 	int c, ovfl;
97880ee5cbfSDavid du Colombier 
97980ee5cbfSDavid du Colombier 	v = 0;
98080ee5cbfSDavid du Colombier 	ovfl = 0;
98180ee5cbfSDavid du Colombier 	for(;;){
98280ee5cbfSDavid du Colombier 		c = *s;
98380ee5cbfSDavid du Colombier 		if(c < '0' || c > '9')
98480ee5cbfSDavid du Colombier 			break;
98580ee5cbfSDavid du Colombier 		s++;
98680ee5cbfSDavid du Colombier 		c -= '0';
98780ee5cbfSDavid du Colombier 		if(v > UlongMax/10 || v == UlongMax/10 && c >= UlongMax%10)
98880ee5cbfSDavid du Colombier 			ovfl = 1;
98980ee5cbfSDavid du Colombier 		v = v * 10 + c;
99080ee5cbfSDavid du Colombier 	}
99180ee5cbfSDavid du Colombier 
99280ee5cbfSDavid du Colombier 	if(e)
99380ee5cbfSDavid du Colombier 		*e = s;
99480ee5cbfSDavid du Colombier 	if(ovfl)
99580ee5cbfSDavid du Colombier 		return UlongMax;
99680ee5cbfSDavid du Colombier 	return v;
99780ee5cbfSDavid du Colombier }
99880ee5cbfSDavid du Colombier 
99980ee5cbfSDavid du Colombier int
100080ee5cbfSDavid du Colombier http11(HConnect *c)
100180ee5cbfSDavid du Colombier {
100280ee5cbfSDavid du Colombier 	return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0;
100380ee5cbfSDavid du Colombier }
100480ee5cbfSDavid du Colombier 
100580ee5cbfSDavid du Colombier char*
100680ee5cbfSDavid du Colombier hmkmimeboundary(HConnect *c)
100780ee5cbfSDavid du Colombier {
100880ee5cbfSDavid du Colombier 	char buf[32];
100980ee5cbfSDavid du Colombier 	int i;
101080ee5cbfSDavid du Colombier 
101180ee5cbfSDavid du Colombier 	srand((time(0)<<16)|getpid());
101280ee5cbfSDavid du Colombier 	strcpy(buf, "upas-");
101380ee5cbfSDavid du Colombier 	for(i = 5; i < sizeof(buf)-1; i++)
101480ee5cbfSDavid du Colombier 		buf[i] = 'a' + nrand(26);
101580ee5cbfSDavid du Colombier 	buf[i] = 0;
101680ee5cbfSDavid du Colombier 	return hstrdup(c, buf);
101780ee5cbfSDavid du Colombier }
101880ee5cbfSDavid du Colombier 
101980ee5cbfSDavid du Colombier HSPairs*
102080ee5cbfSDavid du Colombier hmkspairs(HConnect *c, char *s, char *t, HSPairs *next)
102180ee5cbfSDavid du Colombier {
102280ee5cbfSDavid du Colombier 	HSPairs *sp;
102380ee5cbfSDavid du Colombier 
102480ee5cbfSDavid du Colombier 	sp = halloc(c, sizeof *sp);
102580ee5cbfSDavid du Colombier 	sp->s = s;
102680ee5cbfSDavid du Colombier 	sp->t = t;
102780ee5cbfSDavid du Colombier 	sp->next = next;
102880ee5cbfSDavid du Colombier 	return sp;
102980ee5cbfSDavid du Colombier }
103080ee5cbfSDavid du Colombier 
103180ee5cbfSDavid du Colombier HSPairs*
103280ee5cbfSDavid du Colombier hrevspairs(HSPairs *sp)
103380ee5cbfSDavid du Colombier {
103480ee5cbfSDavid du Colombier 	HSPairs *last, *next;
103580ee5cbfSDavid du Colombier 
103680ee5cbfSDavid du Colombier 	last = nil;
103780ee5cbfSDavid du Colombier 	for(; sp != nil; sp = next){
103880ee5cbfSDavid du Colombier 		next = sp->next;
103980ee5cbfSDavid du Colombier 		sp->next = last;
104080ee5cbfSDavid du Colombier 		last = sp;
104180ee5cbfSDavid du Colombier 	}
104280ee5cbfSDavid du Colombier 	return last;
104380ee5cbfSDavid du Colombier }
104480ee5cbfSDavid du Colombier 
104580ee5cbfSDavid du Colombier HFields*
104680ee5cbfSDavid du Colombier hmkhfields(HConnect *c, char *s, HSPairs *p, HFields *next)
104780ee5cbfSDavid du Colombier {
104880ee5cbfSDavid du Colombier 	HFields *hf;
104980ee5cbfSDavid du Colombier 
105080ee5cbfSDavid du Colombier 	hf = halloc(c, sizeof *hf);
105180ee5cbfSDavid du Colombier 	hf->s = s;
105280ee5cbfSDavid du Colombier 	hf->params = p;
105380ee5cbfSDavid du Colombier 	hf->next = next;
105480ee5cbfSDavid du Colombier 	return hf;
105580ee5cbfSDavid du Colombier }
105680ee5cbfSDavid du Colombier 
105780ee5cbfSDavid du Colombier HFields*
105880ee5cbfSDavid du Colombier hrevhfields(HFields *hf)
105980ee5cbfSDavid du Colombier {
106080ee5cbfSDavid du Colombier 	HFields *last, *next;
106180ee5cbfSDavid du Colombier 
106280ee5cbfSDavid du Colombier 	last = nil;
106380ee5cbfSDavid du Colombier 	for(; hf != nil; hf = next){
106480ee5cbfSDavid du Colombier 		next = hf->next;
106580ee5cbfSDavid du Colombier 		hf->next = last;
106680ee5cbfSDavid du Colombier 		last = hf;
106780ee5cbfSDavid du Colombier 	}
106880ee5cbfSDavid du Colombier 	return last;
106980ee5cbfSDavid du Colombier }
107080ee5cbfSDavid du Colombier 
107180ee5cbfSDavid du Colombier HContent*
107280ee5cbfSDavid du Colombier hmkcontent(HConnect *c, char *generic, char *specific, HContent *next)
107380ee5cbfSDavid du Colombier {
107480ee5cbfSDavid du Colombier 	HContent *ct;
107580ee5cbfSDavid du Colombier 
107680ee5cbfSDavid du Colombier 	ct = halloc(c, sizeof(HContent));
107780ee5cbfSDavid du Colombier 	ct->generic = generic;
107880ee5cbfSDavid du Colombier 	ct->specific = specific;
107980ee5cbfSDavid du Colombier 	ct->next = next;
108080ee5cbfSDavid du Colombier 	ct->q = 1;
108180ee5cbfSDavid du Colombier 	ct->mxb = 0;
108280ee5cbfSDavid du Colombier 	return ct;
108380ee5cbfSDavid du Colombier }
1084