|  | #include <u.h> | 
|  | #include <libc.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	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}, | 
|  | {"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 an clean up junk from a request | 
|  | */ | 
|  | void | 
|  | hreqcleanup(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 void | 
|  | mimeconnection(Hlex *h, char *unused) | 
|  | { | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | hparseheaders(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; | 
|  | } | 
|  | 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 void | 
|  | mimeaccept(Hlex *h, char *name) | 
|  | { | 
|  | h->c->head.oktype = mimeok(h, name, 1, h->c->head.oktype); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeacceptchar(Hlex *h, char *name) | 
|  | { | 
|  | h->c->head.okchar = mimeok(h, name, 0, h->c->head.okchar); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeacceptenc(Hlex *h, char *name) | 
|  | { | 
|  | h->c->head.okencode = mimeok(h, name, 0, h->c->head.okencode); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeacceptlang(Hlex *h, char *name) | 
|  | { | 
|  | h->c->head.oklang = mimeok(h, name, 0, h->c->head.oklang); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimemodified(Hlex *h, char *unused) | 
|  | { | 
|  | lexhead(h); | 
|  | h->c->head.ifmodsince = hdate2sec(h->wordval); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeunmodified(Hlex *h, char *unused) | 
|  | { | 
|  | lexhead(h); | 
|  | h->c->head.ifunmodsince = hdate2sec(h->wordval); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimematch(Hlex *h, char *unused) | 
|  | { | 
|  | h->c->head.ifmatch = mimeetag(h, h->c->head.ifmatch); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimenomatch(Hlex *h, char *unused) | 
|  | { | 
|  | h->c->head.ifnomatch = mimeetag(h, h->c->head.ifnomatch); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * argument is either etag or date | 
|  | */ | 
|  | static void | 
|  | mimeifrange(Hlex *h, char *unused) | 
|  | { | 
|  | int c, d, et; | 
|  |  | 
|  | et = 0; | 
|  | c = getc(h); | 
|  | while(c == ' ' || c == '\t') | 
|  | c = getc(h); | 
|  | if(c == '"') | 
|  | et = 1; | 
|  | else if(c == 'W'){ | 
|  | d = getc(h); | 
|  | if(d == '/') | 
|  | et = 1; | 
|  | ungetc(h); | 
|  | } | 
|  | ungetc(h); | 
|  | if(et){ | 
|  | h->c->head.ifrangeetag = mimeetag(h, h->c->head.ifrangeetag); | 
|  | }else{ | 
|  | lexhead(h); | 
|  | h->c->head.ifrangedate = hdate2sec(h->wordval); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimerange(Hlex *h, char *unused) | 
|  | { | 
|  | h->c->head.range = mimeranges(h, h->c->head.range); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * note: netscape and ie through versions 4.7 and 4 | 
|  | * support only basic authorization, so that is all that is supported here | 
|  | * | 
|  | * "Authorization" ":" "Basic" base64-user-pass | 
|  | * where base64-user-pass is the base64 encoding of | 
|  | * username ":" password | 
|  | */ | 
|  | static void | 
|  | mimeauthorization(Hlex *h, char *unused) | 
|  | { | 
|  | char *up, *p; | 
|  | int n; | 
|  |  | 
|  | if(lex(h) != Word || cistrcmp(h->wordval, "basic") != 0) | 
|  | return; | 
|  |  | 
|  | n = lexbase64(h); | 
|  | if(!n) | 
|  | return; | 
|  |  | 
|  | /* | 
|  | * wipe out source for password, so it won't be logged. | 
|  | * it is replaced by a single =, | 
|  | * which is valid base64, but not ok for an auth reponse. | 
|  | * therefore future parses of the header field will not overwrite | 
|  | * authuser and authpass. | 
|  | */ | 
|  | memmove(h->c->hpos - (n - 1), h->c->hpos, h->c->hstop - h->c->hpos); | 
|  | h->c->hstop -= n - 1; | 
|  | *h->c->hstop = '\0'; | 
|  | h->c->hpos -= n - 1; | 
|  | h->c->hpos[-1] = '='; | 
|  |  | 
|  | up = halloc(h->c, n + 1); | 
|  | n = dec64((uchar*)up, n, h->wordval, n); | 
|  | up[n] = '\0'; | 
|  | p = strchr(up, ':'); | 
|  | if(p != nil){ | 
|  | *p++ = '\0'; | 
|  | h->c->head.authuser = hstrdup(h->c, up); | 
|  | h->c->head.authpass = hstrdup(h->c, p); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeagent(Hlex *h, char *unused) | 
|  | { | 
|  | lexhead(h); | 
|  | h->c->head.client = hstrdup(h->c, h->wordval); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimefrom(Hlex *h, char *unused) | 
|  | { | 
|  | lexhead(h); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimehost(Hlex *h, char *unused) | 
|  | { | 
|  | char *hd; | 
|  |  | 
|  | lexhead(h); | 
|  | for(hd = h->wordval; *hd == ' ' || *hd == '\t'; hd++) | 
|  | ; | 
|  | h->c->head.host = hlower(hstrdup(h->c, hd)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * if present, implies that a message body follows the headers | 
|  | * "content-length" ":" digits | 
|  | */ | 
|  | static void | 
|  | mimecontlen(Hlex *h, char *unused) | 
|  | { | 
|  | char *e; | 
|  | ulong v; | 
|  |  | 
|  | if(lex(h) != Word) | 
|  | return; | 
|  | e = h->wordval; | 
|  | v = digtoul(e, &e); | 
|  | if(v == ~0UL || *e != '\0') | 
|  | return; | 
|  | h->c->head.contlen = v; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * mimexpect	: "expect" ":" expects | 
|  | * expects	: | expects "," expect | 
|  | * expect	: "100-continue" | token | token "=" token expectparams | token "=" qstring expectparams | 
|  | * expectparams	: ";" token | ";" token "=" token | token "=" qstring | 
|  | * for now, we merely parse "100-continue" or anything else. | 
|  | */ | 
|  | static void | 
|  | mimeexpect(Hlex *h, char *unused) | 
|  | { | 
|  | if(lex(h) != Word || cistrcmp(h->wordval, "100-continue") != 0 || lex(h) != '\n') | 
|  | h->c->head.expectother = 1; | 
|  | h->c->head.expectcont = 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimetransenc(Hlex *h, char *unused) | 
|  | { | 
|  | h->c->head.transenc = mimehfields(h); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimefresh(Hlex *h, char *unused) | 
|  | { | 
|  | char *s; | 
|  |  | 
|  | lexhead(h); | 
|  | for(s = h->wordval; *s && (*s==' ' || *s=='\t'); s++) | 
|  | ; | 
|  | if(strncmp(s, "pathstat/", 9) == 0) | 
|  | h->c->head.fresh_thresh = atoi(s+9); | 
|  | else if(strncmp(s, "have/", 5) == 0) | 
|  | h->c->head.fresh_have = atoi(s+5); | 
|  | } | 
|  |  | 
|  | static void | 
|  | mimeignore(Hlex *h, char *unused) | 
|  | { | 
|  | lexhead(h); | 
|  | } | 
|  |  | 
|  | static void | 
|  | parsejump(Hlex *h, char *k) | 
|  | { | 
|  | int l, r, m; | 
|  |  | 
|  | l = 1; | 
|  | r = nelem(mimehead) - 1; | 
|  | while(l <= r){ | 
|  | m = (r + l) >> 1; | 
|  | if(cistrcmp(mimehead[m].name, k) <= 0) | 
|  | l = m + 1; | 
|  | else | 
|  | r = m - 1; | 
|  | } | 
|  | m = l - 1; | 
|  | if(cistrcmp(mimehead[m].name, k) == 0 && !mimehead[m].ignore){ | 
|  | mimehead[m].seen = 1; | 
|  | (*mimehead[m].parse)(h, k); | 
|  | }else | 
|  | mimeignore(h, k); | 
|  | } | 
|  |  | 
|  | static int | 
|  | lex(Hlex *h) | 
|  | { | 
|  | return h->tok = lex1(h, 0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | lexbase64(Hlex *h) | 
|  | { | 
|  | int c, n; | 
|  |  | 
|  | n = 0; | 
|  | lex1(h, 1); | 
|  |  | 
|  | while((c = getc(h)) >= 0){ | 
|  | if(!(c >= 'A' && c <= 'Z' | 
|  | || c >= 'a' && c <= 'z' | 
|  | || c >= '0' && c <= '9' | 
|  | || c == '+' || c == '/')){ | 
|  | ungetc(h); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(n < HMaxWord-1) | 
|  | h->wordval[n++] = c; | 
|  | } | 
|  | h->wordval[n] = '\0'; | 
|  | return n; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * rfc 822/rfc 1521 lexical analyzer | 
|  | */ | 
|  | static int | 
|  | lex1(Hlex *h, int skipwhite) | 
|  | { | 
|  | int level, c; | 
|  |  | 
|  | if(h->eol) | 
|  | return '\n'; | 
|  |  | 
|  | top: | 
|  | c = getc(h); | 
|  | switch(c){ | 
|  | case '(': | 
|  | level = 1; | 
|  | while((c = getc(h)) >= 0){ | 
|  | if(c == '\\'){ | 
|  | c = getc(h); | 
|  | if(c < 0) | 
|  | return '\n'; | 
|  | continue; | 
|  | } | 
|  | if(c == '(') | 
|  | level++; | 
|  | else if(c == ')' && --level == 0) | 
|  | break; | 
|  | else if(c == '\n'){ | 
|  | c = getc(h); | 
|  | if(c < 0) | 
|  | return '\n'; | 
|  | if(c == ')' && --level == 0) | 
|  | break; | 
|  | if(c != ' ' && c != '\t'){ | 
|  | ungetc(h); | 
|  | return '\n'; | 
|  | } | 
|  | } | 
|  | } | 
|  | goto top; | 
|  |  | 
|  | case ' ': case '\t': | 
|  | goto top; | 
|  |  | 
|  | case '\r': | 
|  | c = getc(h); | 
|  | if(c != '\n'){ | 
|  | ungetc(h); | 
|  | goto top; | 
|  | } | 
|  |  | 
|  | case '\n': | 
|  | if(h->tok == '\n'){ | 
|  | h->eol = 1; | 
|  | h->eoh = 1; | 
|  | return '\n'; | 
|  | } | 
|  | c = getc(h); | 
|  | if(c < 0){ | 
|  | h->eol = 1; | 
|  | return '\n'; | 
|  | } | 
|  | if(c != ' ' && c != '\t'){ | 
|  | ungetc(h); | 
|  | h->eol = 1; | 
|  | return '\n'; | 
|  | } | 
|  | goto top; | 
|  |  | 
|  | case ')': | 
|  | case '<': case '>': | 
|  | case '[': case ']': | 
|  | case '@': case '/': | 
|  | case ',': case ';': case ':': case '?': case '=': | 
|  | if(skipwhite){ | 
|  | ungetc(h); | 
|  | return c; | 
|  | } | 
|  | return c; | 
|  |  | 
|  | case '"': | 
|  | if(skipwhite){ | 
|  | ungetc(h); | 
|  | return c; | 
|  | } | 
|  | word(h, "\""); | 
|  | getc(h);		/* skip the closing quote */ | 
|  | return QString; | 
|  |  | 
|  | default: | 
|  | ungetc(h); | 
|  | if(skipwhite) | 
|  | return c; | 
|  | word(h, "\"(){}<>@,;:/[]?=\r\n \t"); | 
|  | if(h->wordval[0] == '\0'){ | 
|  | h->c->head.closeit = 1; | 
|  | hfail(h->c, HSyntax); | 
|  | longjmp(h->jmp, -1); | 
|  | } | 
|  | return Word; | 
|  | } | 
|  | /* not reached */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * return the rest of an rfc 822, including \n | 
|  | * do not map to lower case | 
|  | */ | 
|  | static void | 
|  | lexhead(Hlex *h) | 
|  | { | 
|  | int c, n; | 
|  |  | 
|  | n = 0; | 
|  | while((c = getc(h)) >= 0){ | 
|  | if(c == '\r') | 
|  | c = wordcr(h); | 
|  | else if(c == '\n') | 
|  | c = wordnl(h); | 
|  | if(c == '\n') | 
|  | break; | 
|  | if(c == '\\'){ | 
|  | c = getc(h); | 
|  | if(c < 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(n < HMaxWord-1) | 
|  | h->wordval[n++] = c; | 
|  | } | 
|  | h->tok = '\n'; | 
|  | h->eol = 1; | 
|  | h->wordval[n] = '\0'; | 
|  | } | 
|  |  | 
|  | static void | 
|  | word(Hlex *h, char *stop) | 
|  | { | 
|  | int c, n; | 
|  |  | 
|  | n = 0; | 
|  | while((c = getc(h)) >= 0){ | 
|  | if(c == '\r') | 
|  | c = wordcr(h); | 
|  | else if(c == '\n') | 
|  | c = wordnl(h); | 
|  | if(c == '\\'){ | 
|  | c = getc(h); | 
|  | if(c < 0) | 
|  | break; | 
|  | }else if(c < 32 || strchr(stop, c) != nil){ | 
|  | ungetc(h); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(n < HMaxWord-1) | 
|  | h->wordval[n++] = c; | 
|  | } | 
|  | h->wordval[n] = '\0'; | 
|  | } | 
|  |  | 
|  | static int | 
|  | wordcr(Hlex *h) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = getc(h); | 
|  | if(c == '\n') | 
|  | return wordnl(h); | 
|  | ungetc(h); | 
|  | return ' '; | 
|  | } | 
|  |  | 
|  | static int | 
|  | wordnl(Hlex *h) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = getc(h); | 
|  | if(c == ' ' || c == '\t') | 
|  | return c; | 
|  | ungetc(h); | 
|  |  | 
|  | return '\n'; | 
|  | } | 
|  |  | 
|  | static int | 
|  | getc(Hlex *h) | 
|  | { | 
|  | if(h->eoh) | 
|  | return -1; | 
|  | if(h->c->hpos < h->c->hstop) | 
|  | return *h->c->hpos++; | 
|  | h->eoh = 1; | 
|  | h->eol = 1; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | ungetc(Hlex *h) | 
|  | { | 
|  | if(h->eoh) | 
|  | return; | 
|  | h->c->hpos--; | 
|  | } | 
|  |  | 
|  | static ulong | 
|  | digtoul(char *s, char **e) | 
|  | { | 
|  | ulong v; | 
|  | int c, ovfl; | 
|  |  | 
|  | v = 0; | 
|  | ovfl = 0; | 
|  | for(;;){ | 
|  | c = *s; | 
|  | if(c < '0' || c > '9') | 
|  | break; | 
|  | s++; | 
|  | c -= '0'; | 
|  | if(v > UlongMax/10 || v == UlongMax/10 && c >= UlongMax%10) | 
|  | ovfl = 1; | 
|  | v = v * 10 + c; | 
|  | } | 
|  |  | 
|  | if(e) | 
|  | *e = s; | 
|  | if(ovfl) | 
|  | return UlongMax; | 
|  | return v; | 
|  | } | 
|  |  | 
|  | int | 
|  | http11(HConnect *c) | 
|  | { | 
|  | return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0; | 
|  | } | 
|  |  | 
|  | char* | 
|  | hmkmimeboundary(HConnect *c) | 
|  | { | 
|  | char buf[32]; | 
|  | int i; | 
|  |  | 
|  | srand((time(0)<<16)|getpid()); | 
|  | strcpy(buf, "upas-"); | 
|  | for(i = 5; i < sizeof(buf)-1; i++) | 
|  | buf[i] = 'a' + nrand(26); | 
|  | buf[i] = 0; | 
|  | return hstrdup(c, buf); | 
|  | } | 
|  |  | 
|  | HSPairs* | 
|  | hmkspairs(HConnect *c, char *s, char *t, HSPairs *next) | 
|  | { | 
|  | HSPairs *sp; | 
|  |  | 
|  | sp = halloc(c, sizeof *sp); | 
|  | sp->s = s; | 
|  | sp->t = t; | 
|  | sp->next = next; | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | HSPairs* | 
|  | hrevspairs(HSPairs *sp) | 
|  | { | 
|  | HSPairs *last, *next; | 
|  |  | 
|  | last = nil; | 
|  | for(; sp != nil; sp = next){ | 
|  | next = sp->next; | 
|  | sp->next = last; | 
|  | last = sp; | 
|  | } | 
|  | return last; | 
|  | } | 
|  |  | 
|  | HFields* | 
|  | hmkhfields(HConnect *c, char *s, HSPairs *p, HFields *next) | 
|  | { | 
|  | HFields *hf; | 
|  |  | 
|  | hf = halloc(c, sizeof *hf); | 
|  | hf->s = s; | 
|  | hf->params = p; | 
|  | hf->next = next; | 
|  | return hf; | 
|  | } | 
|  |  | 
|  | HFields* | 
|  | hrevhfields(HFields *hf) | 
|  | { | 
|  | HFields *last, *next; | 
|  |  | 
|  | last = nil; | 
|  | for(; hf != nil; hf = next){ | 
|  | next = hf->next; | 
|  | hf->next = last; | 
|  | last = hf; | 
|  | } | 
|  | return last; | 
|  | } | 
|  |  | 
|  | HContent* | 
|  | hmkcontent(HConnect *c, char *generic, char *specific, HContent *next) | 
|  | { | 
|  | HContent *ct; | 
|  |  | 
|  | ct = halloc(c, sizeof(HContent)); | 
|  | ct->generic = generic; | 
|  | ct->specific = specific; | 
|  | ct->next = next; | 
|  | ct->q = 1; | 
|  | ct->mxb = 0; | 
|  | return ct; | 
|  | } |