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