9 typedef struct Hlex Hlex;
10 typedef struct MimeHead MimeHead;
21 #define UlongMax 4294967295UL
27 int eol; /* end of header line encountered? */
28 uchar *hstart; /* start of header */
29 jmp_buf jmp; /* jmp here to parse header */
30 char wordval[HMaxWord];
37 void (*parse)(Hlex*, char*);
42 static void mimeaccept(Hlex*, char*);
43 static void mimeacceptchar(Hlex*, char*);
44 static void mimeacceptenc(Hlex*, char*);
45 static void mimeacceptlang(Hlex*, char*);
46 static void mimeagent(Hlex*, char*);
47 static void mimeauthorization(Hlex*, char*);
48 static void mimeconnection(Hlex*, char*);
49 static void mimecontlen(Hlex*, char*);
50 static void mimecookie(Hlex*, char*);
51 static void mimeexpect(Hlex*, char*);
52 static void mimefresh(Hlex*, char*);
53 static void mimefrom(Hlex*, char*);
54 static void mimehost(Hlex*, char*);
55 static void mimeifrange(Hlex*, char*);
56 static void mimeignore(Hlex*, char*);
57 static void mimematch(Hlex*, char*);
58 static void mimemodified(Hlex*, char*);
59 static void mimenomatch(Hlex*, char*);
60 static void mimerange(Hlex*, char*);
61 static void mimetransenc(Hlex*, char*);
62 static void mimeunmodified(Hlex*, char*);
65 * headers seen also include
66 * allow cache-control chargeto
67 * content-encoding content-language content-location content-md5 content-range content-type
68 * date etag expires forwarded last-modified max-forwards pragma
69 * proxy-agent proxy-authorization proxy-connection
70 * ua-color ua-cpu ua-os ua-pixels
71 * upgrade via x-afs-tokens x-serial-number
73 static MimeHead mimehead[] =
75 {"accept", mimeaccept},
76 {"accept-charset", mimeacceptchar},
77 {"accept-encoding", mimeacceptenc},
78 {"accept-language", mimeacceptlang},
79 {"authorization", mimeauthorization},
80 {"connection", mimeconnection},
81 {"content-length", mimecontlen},
82 {"cookie", mimecookie},
83 {"expect", mimeexpect},
87 {"if-match", mimematch},
88 {"if-modified-since", mimemodified},
89 {"if-none-match", mimenomatch},
90 {"if-range", mimeifrange},
91 {"if-unmodified-since", mimeunmodified},
93 {"transfer-encoding", mimetransenc},
94 {"user-agent", mimeagent},
98 char* hversion = "HTTP/1.1";
100 static void lexhead(Hlex*);
101 static void parsejump(Hlex*, char*);
102 static int getc(Hlex*);
103 static void ungetc(Hlex*);
104 static int wordcr(Hlex*);
105 static int wordnl(Hlex*);
106 static void word(Hlex*, char*);
107 static int lex1(Hlex*, int);
108 static int lex(Hlex*);
109 static int lexbase64(Hlex*);
110 static ulong digtoul(char *s, char **e);
113 * flush and clean up junk from a request
116 hreqcleanup(HConnect *c)
120 hxferenc(&c->hout, 0);
121 memset(&c->req, 0, sizeof(c->req));
122 memset(&c->head, 0, sizeof(c->head));
124 c->hstop = c->header;
126 for(i = 0; i < nelem(mimehead); i++){
127 mimehead[i].seen = 0;
128 mimehead[i].ignore = 0;
134 * if the client is HTTP/1.0,
135 * ignore headers which match one of the tokens.
136 * restarts parsing if necessary.
139 mimeconnection(Hlex *h, char *)
146 while(lex(h) != Word)
150 if(cistrcmp(h->wordval, "keep-alive") == 0)
151 h->c->head.persist = 1;
152 else if(cistrcmp(h->wordval, "close") == 0)
153 h->c->head.closeit = 1;
154 else if(!http11(h->c)){
155 for(i = 0; i < nelem(mimehead); i++){
156 if(cistrcmp(mimehead[i].name, h->wordval) == 0){
157 reparse = mimehead[i].seen && !mimehead[i].ignore;
158 mimehead[i].ignore = 1;
159 if(cistrcmp(mimehead[i].name, "authorization") == 0){
160 h->c->head.authuser = nil;
161 h->c->head.authpass = nil;
173 * if need to ignore headers we've already parsed,
174 * reset & start over. need to save authorization
175 * info because it's written over when parsed.
178 u = h->c->head.authuser;
179 p = h->c->head.authpass;
180 memset(&h->c->head, 0, sizeof(h->c->head));
181 h->c->head.authuser = u;
182 h->c->head.authpass = p;
184 h->c->hpos = h->hstart;
190 hparseheaders(HConnect *c, int timeout)
194 c->head.fresh_thresh = 0;
195 c->head.fresh_have = 0;
197 if(c->req.vermaj == 0){
198 c->head.host = hmydomain;
202 memset(&h, 0, sizeof(h));
206 if(hgethead(c, 1) < 0)
212 if(setjmp(h.jmp) == -1)
218 while(lex(&h) != '\n'){
219 if(h.tok == Word && lex(&h) == ':')
220 parsejump(&h, hstrdup(c, h.wordval));
228 * according to the http/1.1 spec,
229 * these rules must be followed
231 if(c->head.host == nil){
232 hfail(c, HBadReq, nil);
235 if(c->req.urihost != nil)
236 c->head.host = c->req.urihost;
238 * also need to check host is actually this one
240 }else if(c->head.host == nil)
241 c->head.host = hmydomain;
246 * mimeparams : | mimeparams ";" mimepara
247 * mimeparam : token "=" token | token "=" qstring
259 s = hstrdup(h->c, h->wordval);
260 if(lex(h) != Word && h->tok != QString)
262 p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
264 return hrevspairs(p);
268 * mimehfields : mimehfield | mimehfields commas mimehfield
269 * mimehfield : token mimeparams
270 * commas : "," | commas ","
279 while(lex(h) != Word)
283 f = hmkhfields(h->c, hstrdup(h->c, h->wordval), nil, f);
286 f->params = mimeparams(h);
291 return hrevhfields(f);
295 * parse a list of acceptable types, encodings, languages, etc.
298 mimeok(Hlex *h, char *name, int multipart, HContent *head)
300 char *generic, *specific, *s;
304 * each type is separated by one or more commas
306 while(lex(h) != Word)
310 generic = hstrdup(h->c, h->wordval);
312 if(h->tok == '/' || multipart){
314 * at one time, IE5 improperly said '*' for single types
320 specific = hstrdup(h->c, h->wordval);
321 if(!multipart && strcmp(specific, "*") != 0)
326 head = hmkcontent(h->c, generic, specific, head);
332 * should make a list of these params
333 * for accept, they fall into two classes:
334 * up to a q=..., they modify the media type.
335 * afterwards, they acceptance criteria
338 s = hstrdup(h->c, h->wordval);
339 if(lex(h) != '=' || lex(h) != Word && h->tok != QString)
341 v = strtod(h->wordval, nil);
342 if(strcmp(s, "q") == 0)
344 else if(strcmp(s, "mxb") == 0)
347 /* cope with accept: application/xhtml+xml; profile=http://www.wapforum.org/xhtml, */
348 while(lex(h) == Word || (h->tok != ',' && h->eol == 0) )
350 return mimeok(h, name, multipart, head);
355 return mimeok(h, name, multipart, head);
364 * parse a list of entity tags
366 * entity-tag = [weak] opaque-tag
368 * opaque-tag = quoted-string
371 mimeetag(Hlex *h, HETag *head)
377 while(lex(h) != Word && h->tok != QString)
382 if(h->tok == Word && strcmp(h->wordval, "*") != 0){
383 if(strcmp(h->wordval, "W") != 0)
385 if(lex(h) != '/' || lex(h) != QString)
390 e = halloc(h->c, sizeof(HETag));
391 e->etag = hstrdup(h->c, h->wordval);
402 * ranges-specifier = byte-ranges-specifier
403 * byte-ranges-specifier = "bytes" "=" byte-range-set
404 * byte-range-set = 1#(byte-range-spec|suffix-byte-range-spec)
405 * byte-range-spec = byte-pos "-" [byte-pos]
407 * suffix-byte-range-spec = "-" suffix-length
408 * suffix-length = 1*DIGIT
410 * syntactically invalid range specifiers cause the
411 * entire header field to be ignored.
412 * it is syntactically incorrect for the second byte pos
413 * to be smaller than the first byte pos
416 mimeranges(Hlex *h, HRange *head)
418 HRange *r, *rh, *tail;
423 if(lex(h) != Word || strcmp(h->wordval, "bytes") != 0 || lex(h) != '=')
429 while(lex(h) != Word){
442 start = digtoul(w, &w);
449 stop = digtoul(w, &w);
452 if(!suf && stop < start)
456 r = halloc(h->c, sizeof(HRange));
478 for(tail = head; tail->next != nil; tail = tail->next)
485 mimeaccept(Hlex *h, char *name)
487 h->c->head.oktype = mimeok(h, name, 1, h->c->head.oktype);
491 mimeacceptchar(Hlex *h, char *name)
493 h->c->head.okchar = mimeok(h, name, 0, h->c->head.okchar);
497 mimeacceptenc(Hlex *h, char *name)
499 h->c->head.okencode = mimeok(h, name, 0, h->c->head.okencode);
503 mimeacceptlang(Hlex *h, char *name)
505 h->c->head.oklang = mimeok(h, name, 0, h->c->head.oklang);
509 mimemodified(Hlex *h, char *)
512 h->c->head.ifmodsince = hdate2sec(h->wordval);
516 mimeunmodified(Hlex *h, char *)
519 h->c->head.ifunmodsince = hdate2sec(h->wordval);
523 mimematch(Hlex *h, char *)
525 h->c->head.ifmatch = mimeetag(h, h->c->head.ifmatch);
529 mimenomatch(Hlex *h, char *)
531 h->c->head.ifnomatch = mimeetag(h, h->c->head.ifnomatch);
535 * argument is either etag or date
538 mimeifrange(Hlex *h, char *)
544 while(c == ' ' || c == '\t')
556 h->c->head.ifrangeetag = mimeetag(h, h->c->head.ifrangeetag);
559 h->c->head.ifrangedate = hdate2sec(h->wordval);
564 mimerange(Hlex *h, char *)
566 h->c->head.range = mimeranges(h, h->c->head.range);
570 * parse it like cookies
573 authdigest(Hlex *h, char *)
580 while(lex(h) != Word)
581 if(h->tok != ';' && h->tok != ',')
583 s = hstrdup(h->c, h->wordval);
584 while (lex(h) != Word && h->tok != QString)
587 p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
590 h->c->head.authinfo = hrevspairs(p);
594 * note: netscape and ie through versions 4.7 and 4
595 * support only basic authorization, so that is all that is supported here
597 * "Authorization" ":" "Basic" base64-user-pass
598 * where base64-user-pass is the base64 encoding of
599 * username ":" password
602 authbasic(Hlex *h, char *)
612 * wipe out source for password, so it won't be logged.
613 * it is replaced by a single =,
614 * which is valid base64, but not ok for an auth reponse.
615 * therefore future parses of the header field will not overwrite
616 * authuser and authpass.
618 memmove(h->c->hpos - (n - 1), h->c->hpos, h->c->hstop - h->c->hpos);
619 h->c->hstop -= n - 1;
622 h->c->hpos[-1] = '=';
624 up = halloc(h->c, n + 1);
625 n = dec64((uchar*)up, n, h->wordval, n);
630 h->c->head.authuser = hstrdup(h->c, up);
631 h->c->head.authpass = hstrdup(h->c, p);
636 * "Authorization" ":" "Basic" | "Digest" ...
639 mimeauthorization(Hlex *h, char *)
642 static MimeHead authparser[] = {
643 { "basic", authbasic },
644 { "digest", authdigest },
650 for (i = 0; i < nelem(authparser); i++)
651 if (cistrcmp(h->wordval, authparser[i].name) == 0) {
652 (*authparser[i].parse)(h, nil);
658 mimeagent(Hlex *h, char *)
661 h->c->head.client = hstrdup(h->c, h->wordval);
665 mimefrom(Hlex *h, char *)
671 mimehost(Hlex *h, char *)
676 for(hd = h->wordval; *hd == ' ' || *hd == '\t'; hd++)
678 h->c->head.host = hlower(hstrdup(h->c, hd));
682 * if present, implies that a message body follows the headers
683 * "content-length" ":" digits
686 mimecontlen(Hlex *h, char *)
695 if(v == ~0UL || *e != '\0')
697 h->c->head.contlen = v;
701 * mimexpect : "expect" ":" expects
702 * expects : | expects "," expect
703 * expect : "100-continue" | token | token "=" token expectparams | token "=" qstring expectparams
704 * expectparams : ";" token | ";" token "=" token | token "=" qstring
705 * for now, we merely parse "100-continue" or anything else.
708 mimeexpect(Hlex *h, char *)
710 if(lex(h) != Word || cistrcmp(h->wordval, "100-continue") != 0 || lex(h) != '\n')
711 h->c->head.expectother = 1;
712 h->c->head.expectcont = 1;
716 mimetransenc(Hlex *h, char *)
718 h->c->head.transenc = mimehfields(h);
722 mimecookie(Hlex *h, char *)
729 while(lex(h) != Word)
730 if(h->tok != ';' && h->tok != ',')
732 s = hstrdup(h->c, h->wordval);
733 while (lex(h) != Word && h->tok != QString)
736 p = hmkspairs(h->c, s, hstrdup(h->c, h->wordval), p);
739 h->c->head.cookie = hrevspairs(p);
743 mimefresh(Hlex *h, char *)
748 for(s = h->wordval; *s && (*s==' ' || *s=='\t'); s++)
750 if(strncmp(s, "pathstat/", 9) == 0)
751 h->c->head.fresh_thresh = atoi(s+9);
752 else if(strncmp(s, "have/", 5) == 0)
753 h->c->head.fresh_have = atoi(s+5);
757 mimeignore(Hlex *h, char *)
763 parsejump(Hlex *h, char *k)
768 r = nelem(mimehead) - 1;
771 if(cistrcmp(mimehead[m].name, k) <= 0)
777 if(cistrcmp(mimehead[m].name, k) == 0 && !mimehead[m].ignore){
778 mimehead[m].seen = 1;
779 (*mimehead[m].parse)(h, k);
787 return h->tok = lex1(h, 0);
798 while((c = getc(h)) >= 0){
799 if(!isalnum(c) && c != '+' && c != '/'){
806 h->wordval[n] = '\0';
811 * rfc 822/rfc 1521 lexical analyzer
814 lex1(Hlex *h, int skipwhite)
826 while((c = getc(h)) >= 0){
835 else if(c == ')' && --level == 0)
841 if(c == ')' && --level == 0)
843 if(c != ' ' && c != '\t'){
872 if(c != ' ' && c != '\t'){
883 case ',': case ';': case ':': case '?': case '=':
896 getc(h); /* skip the closing quote */
903 word(h, "\"(){}<>@,;:/[]?=\r\n \t");
904 if(h->wordval[0] == '\0'){
905 h->c->head.closeit = 1;
906 hfail(h->c, HSyntax);
915 * return the rest of an rfc 822, including \n
916 * do not map to lower case
924 while((c = getc(h)) >= 0){
942 h->wordval[n] = '\0';
946 word(Hlex *h, char *stop)
951 while((c = getc(h)) >= 0){
960 }else if(c < 32 || strchr(stop, c) != nil){
968 h->wordval[n] = '\0';
989 if(c == ' ' || c == '\t')
1001 if(h->c->hpos < h->c->hstop)
1002 return *h->c->hpos++;
1017 digtoul(char *s, char **e)
1026 if(c < '0' || c > '9')
1030 if(v > UlongMax/10 || v == UlongMax/10 && c >= UlongMax%10)
1045 return c->req.vermaj > 1 || c->req.vermaj == 1 && c->req.vermin > 0;
1049 hmkmimeboundary(HConnect *c)
1054 srand((time(0)<<16)|getpid());
1055 strcpy(buf, "upas-");
1056 for(i = 5; i < sizeof(buf)-1; i++)
1057 buf[i] = 'a' + nrand(26);
1059 return hstrdup(c, buf);
1063 hmkspairs(HConnect *c, char *s, char *t, HSPairs *next)
1067 sp = halloc(c, sizeof *sp);
1075 hrevspairs(HSPairs *sp)
1077 HSPairs *last, *next;
1080 for(; sp != nil; sp = next){
1089 hmkhfields(HConnect *c, char *s, HSPairs *p, HFields *next)
1093 hf = halloc(c, sizeof *hf);
1101 hrevhfields(HFields *hf)
1103 HFields *last, *next;
1106 for(; hf != nil; hf = next){
1115 hmkcontent(HConnect *c, char *generic, char *specific, HContent *next)
1119 ct = halloc(c, sizeof(HContent));
1120 ct->generic = generic;
1121 ct->specific = specific;