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