1128 lines · 19.6 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ctype.h>#include <libsec.h>#include <bin.h>#include <httpd.h>#include "escape.h" typedef struct Hlex	Hlex;typedef struct MimeHead	MimeHead; enum{	/*	 * tokens	 */	Word	= 1,	QString,}; #define UlongMax	4294967295UL struct Hlex{	int	tok;	int	eoh;	int	eol;			/* end of header line encountered? */	uchar	*hstart;		/* start of header */	jmp_buf	jmp;			/* jmp here to parse header */	char	wordval[HMaxWord];	HConnect *c;}; struct MimeHead{	char	*name;	void	(*parse)(Hlex*, char*);	uchar	seen;	uchar	ignore;}; static void	mimeaccept(Hlex*, char*);static void	mimeacceptchar(Hlex*, char*);static void	mimeacceptenc(Hlex*, char*);static void	mimeacceptlang(Hlex*, char*);static void	mimeagent(Hlex*, char*);static void	mimeauthorization(Hlex*, char*);static void	mimeconnection(Hlex*, char*);static void	mimecontlen(Hlex*, char*);static void	mimecookie(Hlex*, char*);static void	mimeexpect(Hlex*, char*);static void	mimefresh(Hlex*, char*);static void	mimefrom(Hlex*, char*);static void	mimehost(Hlex*, char*);static void	mimeifrange(Hlex*, char*);static void	mimeignore(Hlex*, char*);static void	mimematch(Hlex*, char*);static void	mimemodified(Hlex*, char*);static void	mimenomatch(Hlex*, char*);static void	mimerange(Hlex*, char*);static void	mimetransenc(Hlex*, char*);static void	mimeunmodified(Hlex*, char*); /* * headers seen also include * allow  cache-control chargeto * content-encoding content-language content-location content-md5 content-range content-type * date etag expires forwarded last-modified max-forwards pragma * proxy-agent proxy-authorization proxy-connection * ua-color ua-cpu ua-os ua-pixels * upgrade via x-afs-tokens x-serial-number */static MimeHead	mimehead[] ={	{"accept",		mimeaccept},	{"accept-charset",	mimeacceptchar},	{"accept-encoding",	mimeacceptenc},	{"accept-language",	mimeacceptlang},	{"authorization",	mimeauthorization},	{"connection",		mimeconnection},	{"content-length",	mimecontlen},	{"cookie",		mimecookie},	{"expect",		mimeexpect},	{"fresh",		mimefresh},	{"from",		mimefrom},	{"host",		mimehost},	{"if-match",		mimematch},	{"if-modified-since",	mimemodified},	{"if-none-match",	mimenomatch},	{"if-range",		mimeifrange},	{"if-unmodified-since",	mimeunmodified},	{"range",		mimerange},	{"transfer-encoding",	mimetransenc},	{"user-agent",		mimeagent},}; char*		hmydomain;char*		hversion = "HTTP/1.1"; static	void	lexhead(Hlex*);static	void	parsejump(Hlex*, char*);static	int	getc(Hlex*);static	void	ungetc(Hlex*);static	int	wordcr(Hlex*);static	int	wordnl(Hlex*);static	void	word(Hlex*, char*);static	int	lex1(Hlex*, int);static	int	lex(Hlex*);static	int	lexbase64(Hlex*);static	ulong	digtoul(char *s, char **e); /* * flush and clean up junk from a request */voidhreqcleanup(HConnect *c){	int i; 	hxferenc(&c->hout, 0);	memset(&c->req, 0, sizeof(c->req));	memset(&c->head, 0, sizeof(c->head));	c->hpos = c->header;	c->hstop = c->header;	binfree(&c->bin);	for(i = 0; i < nelem(mimehead); i++){		mimehead[i].seen = 0;		mimehead[i].ignore = 0;	}} /* * list of tokens * if the client is HTTP/1.0, * ignore headers which match one of the tokens. * restarts parsing if necessary. */static voidmimeconnection(Hlex *h, char *){	char *u, *p;	int reparse, i; 	reparse = 0;	for(;;){		while(lex(h) != Word)			if(h->tok != ',')				goto breakout; 		if(cistrcmp(h->wordval, "keep-alive") == 0)			h->c->head.persist = 1;		else if(cistrcmp(h->wordval, "close") == 0)			h->c->head.closeit = 1;		else if(!http11(h->c)){			for(i = 0; i < nelem(mimehead); i++){				if(cistrcmp(mimehead[i].name, h->wordval) == 0){					reparse = mimehead[i].seen && !mimehead[i].ignore;					mimehead[i].ignore = 1;					if(cistrcmp(mimehead[i].name, "authorization") == 0){						h->c->head.authuser = nil;						h->c->head.authpass = nil;					}				}			}		} 		if(lex(h) != ',')			break;	} breakout:;	/*	 * if need to ignore headers we've already parsed,	 * reset & start over.  need to save authorization	 * info because it's written over when parsed.	 */	if(reparse){		u = h->c->head.authuser;		p = h->c->head.authpass;		memset(&h->c->head, 0, sizeof(h->c->head));		h->c->head.authuser = u;		h->c->head.authpass = p; 		h->c->hpos = h->hstart;		longjmp(h->jmp, 1);	}} inthparseheaders(HConnect *c, int timeout){	Hlex h; 	c->head.fresh_thresh = 0;	c->head.fresh_have = 0;	c->head.persist = 0;	if(c->req.vermaj == 0){		c->head.host = hmydomain;		return 1;	} 	memset(&h, 0, sizeof(h));	h.c = c;	if(timeout)		alarm(timeout);	if(hgethead(c, 1) < 0)		return -1;	if(timeout)		alarm(0);	h.hstart = c->hpos; 	if(setjmp(h.jmp) == -1)		return -1; 	h.eol = 0;	h.eoh = 0;	h.tok = '\n';	while(lex(&h) != '\n'){		if(h.tok == Word && lex(&h) == ':')			parsejump(&h, hstrdup(c, h.wordval));		while(h.tok != '\n')			lex(&h);		h.eol = h.eoh;	} 	if(http11(c)){		/*		 * according to the http/1.1 spec,		 * these rules must be followed		 */		if(c->head.host == nil){			hfail(c, HBadReq, nil);			return -1;		}		if(c->req.urihost != nil)			c->head.host = c->req.urihost;		/*		 * also need to check host is actually this one		 */	}else if(c->head.host == nil)		c->head.host = hmydomain;	return 1;} /* * mimeparams	: | mimeparams ";" mimepara * mimeparam	: token "=" token | token "=" qstring */static HSPairs*mimeparams(Hlex *h){	HSPairs *p;	char *s; 	p = nil;	for(;;){		if(lex(h) != Word)			break;		s = hstrdup(h->c, h->wordval);		if(lex(h) != Word && h->tok != QString)			break;		p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);	}	return hrevspairs(p);} /* * mimehfields	: mimehfield | mimehfields commas mimehfield * mimehfield	: token mimeparams * commas	: "," | commas "," */static HFields*mimehfields(Hlex *h){	HFields *f; 	f = nil;	for(;;){		while(lex(h) != Word)			if(h->tok != ',')				goto breakout; 		f = hmkhfields(h->c, hstrdup(h->c, h->wordval), nil, f); 		if(lex(h) == ';')			f->params = mimeparams(h);		if(h->tok != ',')			break;	}breakout:;	return hrevhfields(f);} /* * parse a list of acceptable types, encodings, languages, etc. */static HContent*mimeok(Hlex *h, char *name, int multipart, HContent *head){	char *generic, *specific, *s;	float v; 	/*	 * each type is separated by one or more commas	 */	while(lex(h) != Word)		if(h->tok != ',')			return head; 	generic = hstrdup(h->c, h->wordval);	lex(h);	if(h->tok == '/' || multipart){		/*		 * at one time, IE5 improperly said '*' for single types		 */		if(h->tok != '/')			return nil;		if(lex(h) != Word)			return head;		specific = hstrdup(h->c, h->wordval);		if(!multipart && strcmp(specific, "*") != 0)			return head;		lex(h);	}else		specific = nil;	head = hmkcontent(h->c, generic, specific, head); 	for(;;){		switch(h->tok){		case ';':			/*			 * should make a list of these params			 * for accept, they fall into two classes:			 *	up to a q=..., they modify the media type.			 *	afterwards, they acceptance criteria			 */			if(lex(h) == Word){				s = hstrdup(h->c, h->wordval);				if(lex(h) != '=' || lex(h) != Word && h->tok != QString)					return head;				v = strtod(h->wordval, nil);				if(strcmp(s, "q") == 0)					head->q = v;				else if(strcmp(s, "mxb") == 0)					head->mxb = v;				else{					/* cope with accept: application/xhtml+xml; profile=http://www.wapforum.org/xhtml, */					while(lex(h) == Word || (h->tok != ',' && h->eol == 0) )						;					return mimeok(h, name, multipart, head);				}			}			break;		case ',':			return  mimeok(h, name, multipart, head);		default:			return head;		}		lex(h);	}} /* * parse a list of entity tags * 1#entity-tag * entity-tag = [weak] opaque-tag * weak = "W/" * opaque-tag = quoted-string */static HETag*mimeetag(Hlex *h, HETag *head){	HETag *e;	int weak; 	for(;;){		while(lex(h) != Word && h->tok != QString)			if(h->tok != ',')				return head; 		weak = 0;		if(h->tok == Word && strcmp(h->wordval, "*") != 0){			if(strcmp(h->wordval, "W") != 0)				return head;			if(lex(h) != '/' || lex(h) != QString)				return head;			weak = 1;		} 		e = halloc(h->c, sizeof(HETag));		e->etag = hstrdup(h->c, h->wordval);		e->weak = weak;		e->next = head;		head = e; 		if(lex(h) != ',')			return head;	}} /* * ranges-specifier = byte-ranges-specifier * byte-ranges-specifier = "bytes" "=" byte-range-set * byte-range-set = 1#(byte-range-spec|suffix-byte-range-spec) * byte-range-spec = byte-pos "-" [byte-pos] * byte-pos = 1*DIGIT * suffix-byte-range-spec = "-" suffix-length * suffix-length = 1*DIGIT * * syntactically invalid range specifiers cause the * entire header field to be ignored. * it is syntactically incorrect for the second byte pos * to be smaller than the first byte pos */static HRange*mimeranges(Hlex *h, HRange *head){	HRange *r, *rh, *tail;	char *w;	ulong start, stop;	int suf; 	if(lex(h) != Word || strcmp(h->wordval, "bytes") != 0 || lex(h) != '=')		return head; 	rh = nil;	tail = nil;	for(;;){		while(lex(h) != Word){			if(h->tok != ','){				if(h->tok == '\n')					goto breakout;				return head;			}		} 		w = h->wordval;		start = 0;		suf = 1;		if(w[0] != '-'){			suf = 0;			start = digtoul(w, &w);			if(w[0] != '-')				return head;		}		w++;		stop = ~0UL;		if(w[0] != '\0'){			stop = digtoul(w, &w);			if(w[0] != '\0')				return head;			if(!suf && stop < start)				return head;		} 		r = halloc(h->c, sizeof(HRange));		r->suffix = suf;		r->start = start;		r->stop = stop;		r->next = nil;		if(rh == nil)			rh = r;		else			tail->next = r;		tail = r; 		if(lex(h) != ','){			if(h->tok == '\n')				break;			return head;		}	}breakout:; 	if(head == nil)		return rh; 	for(tail = head; tail->next != nil; tail = tail->next)		;	tail->next = rh;	return head;} static voidmimeaccept(Hlex *h, char *name){	h->c->head.oktype = mimeok(h, name, 1, h->c->head.oktype);} static voidmimeacceptchar(Hlex *h, char *name){	h->c->head.okchar = mimeok(h, name, 0, h->c->head.okchar);} static voidmimeacceptenc(Hlex *h, char *name){	h->c->head.okencode = mimeok(h, name, 0, h->c->head.okencode);}

Lines 1–500 of 1128.