PageRenderTime 8ms CodeModel.GetById 3ms app.highlight 83ms RepoModel.GetById 2ms app.codeStats 0ms

/src/main/jni/polipo/http_parse.c

https://bitbucket.org/madeye/shadowsocks-android
C | 1548 lines | 1406 code | 110 blank | 32 comment | 729 complexity | 44d575fd04c0efeacb5fd0ce27ebf8d1 MD5 | raw file
   1/*
   2Copyright (c) 2003-2006 by Juliusz Chroboczek
   3
   4Permission is hereby granted, free of charge, to any person obtaining a copy
   5of this software and associated documentation files (the "Software"), to deal
   6in the Software without restriction, including without limitation the rights
   7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8copies of the Software, and to permit persons to whom the Software is
   9furnished to do so, subject to the following conditions:
  10
  11The above copyright notice and this permission notice shall be included in
  12all copies or substantial portions of the Software.
  13
  14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20THE SOFTWARE.
  21*/
  22
  23#include "polipo.h"
  24
  25static int getNextWord(const char *buf, int i, int *x_return, int *y_return);
  26static int getNextToken(const char *buf, int i, int *x_return, int *y_return);
  27static int getNextTokenInList(const char *buf, int i, 
  28                              int *x_return, int *y_return,
  29                              int *z_return, int *t_return,
  30                              int *end_return);
  31
  32static AtomPtr atomConnection, atomProxyConnection, atomContentLength,
  33    atomHost, atomAcceptRange, atomTE,
  34    atomReferer, atomProxyAuthenticate, atomProxyAuthorization,
  35    atomKeepAlive, atomTrailer, atomUpgrade, atomDate, atomExpires,
  36    atomIfModifiedSince, atomIfUnmodifiedSince, atomIfRange, atomLastModified,
  37    atomIfMatch, atomIfNoneMatch, atomAge, atomTransferEncoding, 
  38    atomETag, atomCacheControl, atomPragma, atomContentRange, atomRange,
  39    atomVia, atomVary, atomExpect, atomAuthorization,
  40    atomSetCookie, atomCookie, atomCookie2,
  41    atomXPolipoDate, atomXPolipoAccess, atomXPolipoLocation, 
  42    atomXPolipoBodyOffset;
  43
  44AtomPtr atomContentType, atomContentEncoding;
  45
  46int censorReferer = 0;
  47int laxHttpParser = 1;
  48
  49static AtomListPtr censoredHeaders;
  50
  51void
  52preinitHttpParser()
  53{
  54    CONFIG_VARIABLE_SETTABLE(censorReferer, CONFIG_TRISTATE, configIntSetter,
  55                             "Censor referer headers.");
  56    censoredHeaders = makeAtomList(NULL, 0);
  57    if(censoredHeaders == NULL) {
  58        do_log(L_ERROR, "Couldn't allocate censored atoms.\n");
  59        exit(1);
  60    }
  61    CONFIG_VARIABLE(censoredHeaders, CONFIG_ATOM_LIST_LOWER,
  62                    "Headers to censor.");
  63    CONFIG_VARIABLE_SETTABLE(laxHttpParser, CONFIG_BOOLEAN, configIntSetter,
  64                             "Ignore unknown HTTP headers.");
  65}
  66
  67void
  68initHttpParser()
  69{
  70#define A(name, value) name = internAtom(value); if(!name) goto fail;
  71    /* These must be in lower-case */
  72    A(atomConnection, "connection");
  73    A(atomProxyConnection, "proxy-connection");
  74    A(atomContentLength, "content-length");
  75    A(atomHost, "host");
  76    A(atomAcceptRange, "accept-range");
  77    A(atomTE, "te");
  78    A(atomReferer, "referer");
  79    A(atomProxyAuthenticate, "proxy-authenticate");
  80    A(atomProxyAuthorization, "proxy-authorization");
  81    A(atomKeepAlive, "keep-alive");
  82    A(atomTrailer, "trailer");
  83    A(atomUpgrade, "upgrade");
  84    A(atomDate, "date");
  85    A(atomExpires, "expires");
  86    A(atomIfModifiedSince, "if-modified-since");
  87    A(atomIfUnmodifiedSince, "if-unmodified-since");
  88    A(atomIfRange, "if-range");
  89    A(atomLastModified, "last-modified");
  90    A(atomIfMatch, "if-match");
  91    A(atomIfNoneMatch, "if-none-match");
  92    A(atomAge, "age");
  93    A(atomTransferEncoding, "transfer-encoding");
  94    A(atomETag, "etag");
  95    A(atomCacheControl, "cache-control");
  96    A(atomPragma, "pragma");
  97    A(atomContentRange, "content-range");
  98    A(atomRange, "range");
  99    A(atomVia, "via");
 100    A(atomContentType, "content-type");
 101    A(atomContentEncoding, "content-encoding");
 102    A(atomVary, "vary");
 103    A(atomExpect, "expect");
 104    A(atomAuthorization, "authorization");
 105    A(atomSetCookie, "set-cookie");
 106    A(atomCookie, "cookie");
 107    A(atomCookie2, "cookie2");
 108    A(atomXPolipoDate, "x-polipo-date");
 109    A(atomXPolipoAccess, "x-polipo-access");
 110    A(atomXPolipoLocation, "x-polipo-location");
 111    A(atomXPolipoBodyOffset, "x-polipo-body-offset");
 112#undef A
 113    return;
 114
 115 fail:
 116    do_log(L_ERROR, "Couldn't allocate atom.\n");
 117    exit(1);
 118}
 119
 120static int
 121getNextWord(const char *restrict buf, int i, int *x_return, int *y_return)
 122{
 123    int x, y;
 124    while(buf[i] == ' ') i++;
 125    if(buf[i] == '\n' || buf[i] == '\r') return -1;
 126    x = i;
 127    while(buf[i] > 32 && buf[i] < 127) i++;
 128    y = i;
 129
 130    *x_return = x;
 131    *y_return = y;
 132
 133    return 0;
 134}
 135
 136static int
 137skipComment(const char *restrict buf, int i)
 138{
 139    assert(buf[i] == '(');
 140
 141    i++;
 142    while(1) {
 143        if(buf[i] == '\\' && buf[i + 1] == ')') i+=2;
 144        else if(buf[i] == ')') return i + 1;
 145        else if(buf[i] == '\n') {
 146            if(buf[i + 1] == ' ' || buf[i + 1] == '\t')
 147                i += 2;
 148            else
 149                return -1;
 150        } else if(buf[i] == '\r') {
 151            if(buf[i + 1] != '\n') return -1;
 152            if(buf[i + 2] == ' ' || buf[i + 2] == '\t')
 153                i += 3;
 154            else
 155                return -1;
 156        } else {
 157            i++;
 158        }
 159    }
 160    return i;
 161}
 162            
 163
 164static int
 165skipWhitespace(const char *restrict buf, int i)
 166{
 167    while(1) {
 168        if(buf[i] == ' ' || buf[i] == '\t')
 169            i++;
 170        else  if(buf[i] == '(') {
 171            i = skipComment(buf, i);
 172            if(i < 0) return -1;
 173        } else if(buf[i] == '\n') {
 174            if(buf[i + 1] == ' ' || buf[i + 1] == '\t')
 175                i += 2;
 176            else
 177                return i;
 178        } else if(buf[i] == '\r' && buf[i + 1] == '\n') {
 179            if(buf[i + 2] == ' ' || buf[i + 2] == '\t')
 180                i += 3;
 181            else
 182                return i;
 183        } else
 184            return i;
 185    }
 186}
 187
 188static int
 189getNextToken(const char *restrict buf, int i, int *x_return, int *y_return)
 190{
 191    int x, y;
 192 again:
 193    while(buf[i] == ' ' || buf[i] == '\t')
 194        i++;
 195    if(buf[i] == '(') {
 196        i++;
 197        while(buf[i] != ')') {
 198            if(buf[i] == '\n' || buf[i] == '\r')
 199                return -1;
 200            if(buf[i] == '\\' && buf[i + 1] != '\n' && buf[i + 1] != '\r')
 201                buf += 2;
 202            else
 203                buf++;
 204        }
 205        goto again;
 206    }
 207    if(buf[i] == '\n') {
 208        if(buf[i + 1] == ' ' || buf[i + 1] == '\t') {
 209            i += 2;
 210            goto again;
 211        } else {
 212            return -1;
 213        }
 214    }
 215    if(buf[i] == '\r') {
 216        if(buf[i + 1] == '\n' && (buf[i + 2] == ' ' || buf[i + 2] == '\t')) {
 217            i += 3;
 218            goto again;
 219        } else {
 220            return -1;
 221        }
 222    }
 223    x = i;
 224    while(buf[i] > 32 && buf[i] < 127) {
 225        switch(buf[i]) {
 226        case '(': case ')': case '<': case '>': case '@':
 227        case ',': case ';': case ':': case '\\': case '/':
 228        case '[': case ']': case '?': case '=':
 229        case '{': case '}': case ' ': case '\t':
 230            goto out;
 231        default:
 232            i++;
 233        }
 234    }
 235 out:
 236    y = i;
 237
 238    *x_return = x;
 239    *y_return = y;
 240
 241    return y;
 242}
 243
 244static int
 245getNextETag(const char * restrict buf, int i, 
 246            int *x_return, int *y_return, int *weak_return)
 247{
 248    int weak = 0;
 249    int x, y;
 250    while(buf[i] == ' ' || buf[i] == '\t')
 251        i++;
 252    if(buf[i] == 'W' && buf[i + 1] == '/') {
 253        weak = 1;
 254        i += 2;
 255    }
 256    if(buf[i] == '"')
 257        i++;
 258    else
 259        return -1;
 260
 261    x = i;
 262    while(buf[i] != '"') {
 263        if(buf[i] == '\r' || buf[i] == '\n')
 264            return -1;
 265        i++;
 266    }
 267    y = i;
 268    i++;
 269
 270    *x_return = x;
 271    *y_return = y;
 272    *weak_return = weak;
 273    return i;
 274}
 275
 276static int
 277getNextTokenInList(const char *restrict buf, int i, 
 278                   int *x_return, int *y_return,
 279                   int *z_return, int *t_return,
 280                   int *end_return)
 281{
 282    int j, x, y, z = -1, t = -1, end;
 283    j = getNextToken(buf, i, &x, &y);
 284    if(j < 0)
 285        return -1;
 286    while(buf[j] == ' ' || buf[j] == '\t')
 287        j++;
 288
 289    if(buf[j] == '=') {
 290        j++;
 291        while(buf[j] == ' ' || buf[j] == '\t')
 292            j++;
 293        z = j;
 294        while(buf[j] != ',' && buf[j] != '\n' && buf[j] != '\r')
 295            j++;
 296    }
 297
 298    if(buf[j] == '\n' || buf[j] == '\r') {
 299        if(buf[j] == '\r') {
 300            if(buf[j + 1] != '\n')
 301                return -1;
 302            j += 2;
 303        } else
 304            j++;
 305        end = 1;
 306        if(buf[j] == ' ' || buf[j] == '\t') {
 307            while(buf[j] == ' ' || buf[j] == '\t')
 308                j++;
 309            end = 0;
 310        }
 311    } else if(buf[j] == ',') {
 312        j++;
 313        while(buf[j] == ' ' || buf[j] == '\t')
 314            j++;
 315        end = 0;
 316    } else {
 317        return -1;
 318    }
 319
 320    *x_return = x;
 321    *y_return = y;
 322    if(z_return)
 323        *z_return = z;
 324    if(t_return)
 325        *t_return = t;
 326    *end_return = end;
 327    return j;
 328}
 329
 330static inline int
 331token_compare(const char *buf, int start, int end, const char *s)
 332{
 333    return (strcasecmp_n(s, buf + start, end - start) == 0);
 334}
 335
 336static int
 337skipEol(const char *restrict buf, int i)
 338{
 339    while(buf[i] == ' ')
 340        i++;
 341    if(buf[i] == '\n')
 342        return i + 1;
 343    else if(buf[i] == '\r') {
 344        if(buf[i + 1] == '\n')
 345            return i + 2;
 346        else
 347            return -1;
 348    } else {
 349        return -1;
 350    }
 351}
 352    
 353static int
 354skipToEol(const char *restrict buf, int i, int *start_return)
 355{
 356    while(buf[i] != '\n' && buf[i] != '\r')
 357        i++;
 358    if(buf[i] == '\n') {
 359        *start_return = i;
 360        return i + 1;
 361    } else if(buf[i] == '\r') {
 362        if(buf[i + 1] == '\n') {
 363            *start_return = i;
 364            return i + 2;
 365        } else {
 366            return -1;
 367        }
 368    }
 369    return -1;
 370}
 371
 372static int
 373getHeaderValue(const char *restrict buf, int start, 
 374               int *value_start_return, int *value_end_return)
 375{
 376    int i, j, k;
 377
 378    while(buf[start] == ' ' || buf[start] == '\t')
 379        start++;
 380    i = start;
 381 again:
 382    j = skipToEol(buf, i, &k);
 383    if(j < 0)
 384        return -1;
 385    if(buf[j] == ' ' || buf[j] == '\t') {
 386        i = j + 1;
 387        goto again;
 388    }
 389    *value_start_return = start;
 390    *value_end_return = k;
 391    return j;
 392}
 393    
 394int
 395httpParseClientFirstLine(const char *restrict buf, int offset,
 396                         int *method_return,
 397                         AtomPtr *url_return,
 398                         int *version_return)
 399{
 400    int i = 0;
 401    int x, y;
 402    int method;
 403    AtomPtr url;
 404    int version = HTTP_UNKNOWN;
 405    int eol;
 406
 407    i = offset;
 408    i = getNextWord(buf, i, &x, &y);
 409    if(i < 0) return -1;
 410    if(y == x + 3 && memcmp(buf + x, "GET", 3) == 0)
 411        method = METHOD_GET;
 412    else if(y == x + 4 && memcmp(buf + x, "HEAD", 4) == 0)
 413        method = METHOD_HEAD;
 414    else if(y == x + 4 && memcmp(buf + x, "POST", 4) == 0)
 415        method = METHOD_POST;
 416    else if(y == x + 3 && memcmp(buf + x, "PUT", 3) == 0)
 417        method = METHOD_PUT;
 418    else if(y == x + 7 && memcmp(buf + x, "CONNECT", 7) == 0)
 419        method = METHOD_CONNECT;
 420    else
 421        method = METHOD_UNKNOWN;
 422
 423    i = getNextWord(buf, y + 1, &x, &y);
 424    if(i < 0) return -1;
 425
 426    url = internAtomN(buf + x, y - x);
 427
 428    i = getNextWord(buf, y + 1, &x, &y);
 429    if(i < 0) {
 430        releaseAtom(url);
 431        return -1;
 432    }
 433
 434    if(y == x + 8) {
 435        if(memcmp(buf + x, "HTTP/1.", 7) != 0)
 436            version = HTTP_UNKNOWN;
 437        else if(buf[x + 7] == '0')
 438            version = HTTP_10;
 439        else if(buf[x + 7] >= '1' && buf[x + 7] <= '9')
 440            version = HTTP_11;
 441        else
 442            version = HTTP_UNKNOWN;
 443    }
 444
 445    eol = skipEol(buf, y);
 446    if(eol < 0) return -1;
 447        
 448    *method_return = method;
 449    if(url_return)
 450        *url_return = url;
 451    else
 452        releaseAtom(url);
 453    *version_return = version;
 454    return eol;
 455}
 456
 457int
 458httpParseServerFirstLine(const char *restrict buf, 
 459                         int *status_return,
 460                         int *version_return,
 461                         AtomPtr *message_return)
 462{
 463    int i = 0;
 464    int x, y, eol;
 465    int status;
 466    int version = HTTP_UNKNOWN;
 467    
 468    i = getNextWord(buf, 0, &x, &y);
 469    if(i < 0)
 470        return -1;
 471    if(y == x + 8 && memcmp(buf + x, "HTTP/1.0", 8) == 0)
 472        version = HTTP_10;
 473    else if(y >= x + 8 && memcmp(buf + x, "HTTP/1.", 7) == 0)
 474        version = HTTP_11;
 475    else
 476        version = HTTP_UNKNOWN;
 477
 478    i = getNextWord(buf, y + 1, &x, &y);
 479    if(i < 0) return -1;
 480    if(y == x + 3)
 481        status = atol(buf + x);
 482    else return -1;
 483
 484    i = skipToEol(buf, y, &eol);
 485    if(i < 0) return -1;
 486        
 487    *status_return = status;
 488    *version_return = version;
 489    if(message_return) {
 490        /* Netscape enterprise bug */
 491        if(eol > y)
 492            *message_return = internAtomN(buf + y + 1, eol - y - 1);
 493        else
 494            *message_return = internAtom("No message");
 495    }
 496    return i;
 497}
 498
 499static int
 500parseInt(const char *restrict buf, int start, int *val_return)
 501{
 502    int i = start, val = 0;
 503    if(!digit(buf[i]))
 504        return -1;
 505    while(digit(buf[i])) {
 506        val = val * 10 + (buf[i] - '0');
 507        i++;
 508    }
 509    *val_return = val;
 510    return i;
 511}
 512
 513/* Returned *name_start_return is -1 at end of headers, -2 if the line
 514   couldn't be parsed. */
 515static int
 516parseHeaderLine(const char *restrict buf, int start,
 517                int *name_start_return, int *name_end_return,
 518                int *value_start_return, int *value_end_return)
 519{
 520    int i;
 521    int name_start, name_end, value_start, value_end;
 522
 523    if(buf[start] == '\n') {
 524        *name_start_return = -1;
 525        return start + 1;
 526    }
 527    if(buf[start] == '\r' && buf[start + 1] == '\n') {
 528        *name_start_return = -1;
 529        return start + 2;
 530    }
 531
 532    i = getNextToken(buf, start, &name_start, &name_end);
 533    if(i < 0 || buf[i] != ':')
 534        goto syntax;
 535    i++;
 536    while(buf[i] == ' ' || buf[i] == '\t')
 537        i++;
 538
 539    i = getHeaderValue(buf, i, &value_start, &value_end);
 540    if(i < 0)
 541        goto syntax;
 542
 543    *name_start_return = name_start;
 544    *name_end_return = name_end;
 545    *value_start_return = value_start;
 546    *value_end_return = value_end;
 547    return i;
 548
 549 syntax:
 550    i = start;
 551    while(1) {
 552        if(buf[i] == '\n') {
 553            i++;
 554            break;
 555        }
 556        if(buf[i] == '\r' && buf[i + 1] == '\n') {
 557            i += 2;
 558            break;
 559        }
 560        i++;
 561    }
 562    *name_start_return = -2;
 563    return i;
 564}
 565
 566int
 567findEndOfHeaders(const char *restrict buf, int from, int to, int *body_return) 
 568{
 569    int i = from;
 570    int eol = 0;
 571    while(i < to) {
 572        if(buf[i] == '\n') {
 573            if(eol) {
 574                *body_return = i + 1;
 575                return eol;
 576            }
 577            eol = i;
 578            i++;
 579        } else if(buf[i] == '\r') {
 580            if(i < to - 1 && buf[i + 1] == '\n') {
 581                if(eol) {
 582                    *body_return = eol;
 583                    return i + 2;
 584                }
 585                eol = i;
 586                i += 2;
 587            } else {
 588                eol = 0;
 589                i++;
 590            }
 591        } else {
 592            eol = 0;
 593            i++;
 594        }
 595    }
 596    return -1;
 597}
 598
 599static int
 600parseContentRange(const char *restrict buf, int i, 
 601                  int *from_return, int *to_return, int *full_len_return)
 602{
 603    int j;
 604    int from, to, full_len;
 605
 606    i = skipWhitespace(buf, i);
 607    if(i < 0) return -1;
 608    if(!token_compare(buf, i, i + 5, "bytes")) {
 609        do_log(L_WARN, "Incorrect Content-Range header -- chugging along.\n");
 610    } else {
 611        i += 5;
 612    }
 613    i = skipWhitespace(buf, i);
 614    if(buf[i] == '*') {
 615        from = 0;
 616        to = -1;
 617        i++;
 618    } else {
 619        i = parseInt(buf, i, &from);
 620        if(i < 0) return -1;
 621        if(buf[i] != '-') return -1;
 622        i++;
 623        i = parseInt(buf, i, &to);
 624        if(i < 0) return -1;
 625        to = to + 1;
 626    }
 627    if(buf[i] != '/')
 628        return -1;
 629    i++;
 630    if(buf[i] == '*')
 631        full_len = -1;
 632    else {
 633        i = parseInt(buf, i, &full_len);
 634        if(i < 0) return -1;
 635    }
 636    j = skipEol(buf, i);
 637    if(j < 0)
 638        return -1;
 639
 640    *from_return = from;
 641    *to_return = to;
 642    *full_len_return = full_len;
 643    return i;
 644}
 645
 646static int
 647parseRange(const char *restrict buf, int i, 
 648           int *from_return, int *to_return)
 649{
 650    int j;
 651    int from, to;
 652
 653    i = skipWhitespace(buf, i);
 654    if(i < 0)
 655        return -1;
 656    if(!token_compare(buf, i, i + 6, "bytes="))
 657        return -1;
 658    i += 6;
 659    i = skipWhitespace(buf, i);
 660    if(buf[i] == '-') {
 661        from = 0;
 662    } else {
 663        i = parseInt(buf, i, &from);
 664        if(i < 0) return -1;
 665    }
 666    if(buf[i] != '-')
 667        return -1;
 668    i++;
 669    j = parseInt(buf, i, &to);
 670    if(j < 0) 
 671        to = -1;
 672    else {
 673        to = to + 1;
 674        i = j;
 675    }
 676    j = skipEol(buf, i);
 677    if(j < 0) return -1;
 678    *from_return = from;
 679    *to_return = to;
 680    return i;
 681}
 682
 683static void
 684parseCacheControl(const char *restrict buf, 
 685                  int token_start, int token_end,
 686                  int v_start, int v_end, int *age_return)
 687{
 688    if(v_start <= 0 || !digit(buf[v_start])) {
 689        do_log(L_WARN, "Couldn't parse Cache-Control: ");
 690        do_log_n(L_WARN, buf + token_start,
 691                 (v_end >= 0 ? v_end : token_end) -
 692                 token_start);
 693        do_log(L_WARN, "\n");
 694    } else
 695        *age_return = atoi(buf + v_start);
 696}
 697
 698static int
 699urlSameHost(const char *url1, int len1, const char *url2, int len2)
 700{
 701    int i;
 702    if(len1 < 7 || len2 < 7)
 703        return 0;
 704    if(memcmp(url1 + 4, "://", 3) != 0 || memcmp(url2 + 4, "://", 3) != 0)
 705        return 0;
 706
 707    i = 7;
 708    while(i < len1 && i < len2 && url1[i] != '/' && url2[i] != '/') {
 709        if((url1[i] | 0x20) != (url2[i] | 0x20))
 710            break;
 711        i++;
 712    }
 713
 714    if((i == len1 || url1[i] == '/') && ((i == len2 || url2[i] == '/')))
 715        return 1;
 716    return 0;
 717}
 718
 719static char *
 720resize_hbuf(char *hbuf, int *size, char *hbuf_small)
 721{
 722    int new_size = 2 * *size;
 723    char *new_hbuf;
 724
 725    if(new_size <= *size)
 726        goto fail;
 727
 728    if(hbuf == hbuf_small) {
 729        new_hbuf = malloc(new_size);
 730        if(new_hbuf == NULL) goto fail;
 731        memcpy(new_hbuf, hbuf, *size);
 732    } else {
 733        new_hbuf = realloc(hbuf, new_size);
 734        if(new_hbuf == NULL) goto fail;
 735    }
 736    *size = new_size;
 737    return new_hbuf;
 738
 739 fail:
 740    if(hbuf != hbuf_small)
 741        free(hbuf);
 742    *size = 0;
 743    return NULL;
 744}
 745
 746int
 747httpParseHeaders(int client, AtomPtr url,
 748                 const char *buf, int start, HTTPRequestPtr request,
 749                 AtomPtr *headers_return,
 750                 int *len_return, CacheControlPtr cache_control_return,
 751                 HTTPConditionPtr *condition_return, int *te_return,
 752                 time_t *date_return, time_t *last_modified_return,
 753                 time_t *expires_return, time_t *polipo_age_return,
 754                 time_t *polipo_access_return, int *polipo_body_offset_return,
 755                 int *age_return, char **etag_return, AtomPtr *expect_return,
 756                 HTTPRangePtr range_return, HTTPRangePtr content_range_return,
 757                 char **location_return, AtomPtr *via_return,
 758                 AtomPtr *auth_return)
 759{
 760    int local = url ? urlIsLocal(url->string, url->length) : 0;
 761    char hbuf_small[512];
 762    char *hbuf = hbuf_small;
 763    int hbuf_size = 512, hbuf_length = 0;
 764    int i, j,
 765        name_start, name_end, value_start, value_end, 
 766        token_start, token_end, end;
 767    AtomPtr name = NULL;
 768    time_t date = -1, last_modified = -1, expires = -1, polipo_age = -1,
 769        polipo_access = -1, polipo_body_offset = -1;
 770    int len = -1;
 771    CacheControlRec cache_control;
 772    char *endptr;
 773    int te = TE_IDENTITY;
 774    int age = -1;
 775    char *etag = NULL, *ifrange = NULL;
 776    int persistent = (!request || (request->connection->version != HTTP_10));
 777    char *location = NULL;
 778    AtomPtr via = NULL;
 779    AtomPtr auth = NULL;
 780    AtomPtr expect = NULL;
 781    HTTPConditionPtr condition;
 782    time_t ims = -1, inms = -1;
 783    char *im = NULL, *inm = NULL;
 784    AtomListPtr hopToHop = NULL;
 785    HTTPRangeRec range = {-1, -1, -1}, content_range = {-1, -1, -1};
 786    int haveCacheControl = 0;
 787 
 788#define RESIZE_HBUF() \
 789    do { \
 790        hbuf = resize_hbuf(hbuf, &hbuf_size, hbuf_small); \
 791        if(hbuf == NULL) \
 792            goto fail; \
 793    } while(0)
 794
 795    cache_control.flags = 0;
 796    cache_control.max_age = -1;
 797    cache_control.s_maxage = -1;
 798    cache_control.min_fresh = -1;
 799    cache_control.max_stale = -1;
 800    
 801    i = start;
 802
 803    while(1) {
 804        i = parseHeaderLine(buf, i,
 805                            &name_start, &name_end, &value_start, &value_end);
 806        if(i < 0) {
 807            do_log(L_ERROR, "Couldn't find end of header line.\n");
 808            goto fail;
 809        }
 810
 811        if(name_start == -1)
 812            break;
 813
 814        if(name_start < 0)
 815            continue;
 816
 817        name = internAtomLowerN(buf + name_start, name_end - name_start);
 818
 819        if(name == atomConnection) {
 820            j = getNextTokenInList(buf, value_start, 
 821                                   &token_start, &token_end, NULL, NULL,
 822                                   &end);
 823            while(1) {
 824                if(j < 0) {
 825                    do_log(L_ERROR, "Couldn't parse Connection: ");
 826                    do_log_n(L_ERROR, buf + value_start, 
 827                             value_end - value_start);
 828                    do_log(L_ERROR, ".\n");
 829                    goto fail;
 830                }
 831                if(token_compare(buf, token_start, token_end, "close")) {
 832                    persistent = 0;
 833                } else if(token_compare(buf, token_start, token_end, 
 834                                        "keep-alive")) {
 835                    persistent = 1;
 836                } else {
 837                    if(hopToHop == NULL)
 838                        hopToHop = makeAtomList(NULL, 0);
 839                    if(hopToHop == NULL) {
 840                        do_log(L_ERROR, "Couldn't allocate atom list.\n");
 841                        goto fail;
 842                    }
 843                    atomListCons(internAtomLowerN(buf + token_start,
 844                                                  token_end - token_start),
 845                                 hopToHop);
 846                }
 847                if(end)
 848                    break;
 849                j = getNextTokenInList(buf, j, 
 850                                       &token_start, &token_end, NULL, NULL,
 851                                       &end);
 852            }
 853        } else if(name == atomCacheControl)
 854            haveCacheControl = 1;
 855
 856        releaseAtom(name);
 857        name = NULL;
 858    }
 859    
 860    i = start;
 861
 862    while(1) {
 863        i = parseHeaderLine(buf, i, 
 864                            &name_start, &name_end, &value_start, &value_end);
 865        if(i < 0) {
 866            do_log(L_ERROR, "Couldn't find end of header line.\n");
 867            goto fail;
 868        }
 869
 870        if(name_start == -1)
 871            break;
 872
 873        if(name_start < 0) {
 874            do_log(L_WARN, "Couldn't parse header line.\n");
 875            if(laxHttpParser)
 876                continue;
 877            else
 878                goto fail;
 879        }
 880
 881        name = internAtomLowerN(buf + name_start, name_end - name_start);
 882        
 883        if(name == atomProxyConnection) {
 884            j = getNextTokenInList(buf, value_start, 
 885                                   &token_start, &token_end, NULL, NULL,
 886                                   &end);
 887            while(1) {
 888                if(j < 0) {
 889                    do_log(L_WARN, "Couldn't parse Proxy-Connection:");
 890                    do_log_n(L_WARN, buf + value_start, 
 891                             value_end - value_start);
 892                    do_log(L_WARN, ".\n");
 893                    persistent = 0;
 894                    break;
 895                }
 896                if(token_compare(buf, token_start, token_end, "close")) {
 897                    persistent = 0;
 898                } else if(token_compare(buf, token_start, token_end, 
 899                                        "keep-alive")) {
 900                    persistent = 1;
 901                }
 902                if(end)
 903                    break;
 904                j = getNextTokenInList(buf, j, 
 905                                       &token_start, &token_end, NULL, NULL,
 906                                       &end);
 907            }
 908        } else if(name == atomContentLength) {
 909            j = skipWhitespace(buf, value_start);
 910            if(j < 0) {
 911                do_log(L_WARN, "Couldn't parse Content-Length: \n");
 912                do_log_n(L_WARN, buf + value_start, value_end - value_start);
 913                do_log(L_WARN, ".\n");
 914                len = -1;
 915            } else {
 916                errno = 0;
 917                len = strtol(buf + value_start, &endptr, 10);
 918                if(errno == ERANGE || endptr <= buf + value_start) {
 919                    do_log(L_WARN, "Couldn't parse Content-Length: \n");
 920                    do_log_n(L_WARN, buf + value_start, 
 921                             value_end - value_start);
 922                    do_log(L_WARN, ".\n");
 923                    len = -1;
 924                }
 925            }
 926        } else if((!local && name == atomProxyAuthorization) ||
 927                  (local && name == atomAuthorization)) {
 928            if(auth_return) {
 929                auth = internAtomN(buf + value_start, value_end - value_start);
 930                if(auth == NULL) {
 931                    do_log(L_ERROR, "Couldn't allocate authorization.\n");
 932                    goto fail;
 933                }
 934            }
 935        } else if(name == atomReferer) {
 936            int h;
 937            if(censorReferer == 0 || 
 938               (censorReferer == 1 && url != NULL &&
 939                urlSameHost(url->string, url->length,
 940                            buf + value_start, value_end - value_start))) {
 941                while(hbuf_length > hbuf_size - 2)
 942                    RESIZE_HBUF();
 943                hbuf[hbuf_length++] = '\r';
 944                hbuf[hbuf_length++] = '\n';
 945                do {
 946                    h = snnprint_n(hbuf, hbuf_length, hbuf_size,
 947                                   buf + name_start, value_end - name_start);
 948                    if(h < 0) RESIZE_HBUF();
 949                } while(h < 0);
 950                hbuf_length = h;
 951            }
 952        } else if(name == atomTrailer || name == atomUpgrade) {
 953            do_log(L_ERROR, "Trailers or upgrade present.\n");
 954            goto fail;
 955        } else if(name == atomDate || name == atomExpires ||
 956                  name == atomIfModifiedSince || 
 957                  name == atomIfUnmodifiedSince ||
 958                  name == atomLastModified ||
 959                  name == atomXPolipoDate || name == atomXPolipoAccess) {
 960            time_t t;
 961            j = parse_time(buf, value_start, value_end, &t);
 962            if(j < 0) {
 963                if(name != atomExpires) {
 964                    do_log(L_WARN, "Couldn't parse %s: ", name->string);
 965                    do_log_n(L_WARN, buf + value_start,
 966                             value_end - value_start);
 967                    do_log(L_WARN, "\n");
 968                }
 969                t = -1;
 970            }
 971            if(name == atomDate) {
 972                if(t >= 0)
 973                    date = t;
 974            } else if(name == atomExpires) {
 975                if(t >= 0)
 976                    expires = t;
 977                else
 978                    expires = 0;
 979            } else if(name == atomLastModified)
 980                last_modified = t;
 981            else if(name == atomIfModifiedSince)
 982                ims = t;
 983            else if(name == atomIfUnmodifiedSince)
 984                inms = t;
 985            else if(name == atomXPolipoDate)
 986                polipo_age = t;
 987            else if(name == atomXPolipoAccess)
 988                polipo_access = t;
 989        } else if(name == atomAge) {
 990            j = skipWhitespace(buf, value_start);
 991            if(j < 0) {
 992                age = -1;
 993            } else {
 994                errno = 0;
 995                age = strtol(buf + value_start, &endptr, 10);
 996                if(errno == ERANGE || endptr <= buf + value_start)
 997                    age = -1;
 998            }
 999            if(age < 0) {
1000                do_log(L_WARN, "Couldn't parse age: \n");
1001                do_log_n(L_WARN, buf + value_start, value_end - value_start);
1002                do_log(L_WARN, " -- ignored.\n");
1003            }
1004        } else if(name == atomXPolipoBodyOffset) {
1005            j = skipWhitespace(buf, value_start);
1006            if(j < 0) {
1007                do_log(L_ERROR, "Couldn't parse body offset.\n");
1008                goto fail;
1009            } else {
1010                errno = 0;
1011                polipo_body_offset = strtol(buf + value_start, &endptr, 10);
1012                if(errno == ERANGE || endptr <= buf + value_start) {
1013                    do_log(L_ERROR, "Couldn't parse body offset.\n");
1014                    goto fail;
1015                }
1016            }
1017        } else if(name == atomTransferEncoding) {
1018            if(token_compare(buf, value_start, value_end, "identity"))
1019                te = TE_IDENTITY;
1020            else if(token_compare(buf, value_start, value_end, "chunked"))
1021                te = TE_CHUNKED;
1022            else
1023                te = TE_UNKNOWN;
1024        } else if(name == atomETag ||
1025                  name == atomIfNoneMatch || name == atomIfMatch ||
1026                  name == atomIfRange) {
1027            int x, y;
1028            int weak;
1029            char *e;
1030            j = getNextETag(buf, value_start, &x, &y, &weak);
1031            if(j < 0) {
1032                if(buf[value_start] != '\r' && buf[value_start] != '\n')
1033                    do_log(L_ERROR, "Couldn't parse ETag.\n");
1034            } else if(weak) {
1035                do_log(L_WARN, "Server returned weak ETag -- ignored.\n");
1036            } else {
1037                e = strdup_n(buf + x, y - x);
1038                if(e == NULL) goto fail;
1039                if(name == atomETag) {
1040                    if(!etag)
1041                        etag = e;
1042                    else
1043                        free(e);
1044                } else if(name == atomIfNoneMatch) {
1045                    if(!inm)
1046                        inm = e;
1047                    else
1048                        free(e);
1049                } else if(name == atomIfMatch) {
1050                    if(!im)
1051                        im = e;
1052                    else
1053                        free(e);
1054                } else if(name == atomIfRange) {
1055                    if(!ifrange)
1056                        ifrange = e;
1057                    else
1058                        free(e);
1059                } else {
1060                    abort();
1061                }
1062            }
1063        } else if(name == atomCacheControl) {
1064            int v_start, v_end;
1065            j = getNextTokenInList(buf, value_start, 
1066                                   &token_start, &token_end, 
1067                                   &v_start, &v_end,
1068                                   &end);
1069            while(1) {
1070                if(j < 0) {
1071                    do_log(L_WARN, "Couldn't parse Cache-Control.\n");
1072                    cache_control.flags |= CACHE_NO;
1073                    break;
1074                }
1075                if(token_compare(buf, token_start, token_end, "no-cache")) {
1076                    cache_control.flags |= CACHE_NO;
1077                } else if(token_compare(buf, token_start, token_end,
1078                                        "public")) {
1079                    cache_control.flags |= CACHE_PUBLIC;
1080                } else if(token_compare(buf, token_start, token_end, 
1081                                        "private")) {
1082                    cache_control.flags |= CACHE_PRIVATE;
1083                } else if(token_compare(buf, token_start, token_end, 
1084                                        "no-store")) {
1085                    cache_control.flags |= CACHE_NO_STORE;
1086                } else if(token_compare(buf, token_start, token_end, 
1087                                        "no-transform")) {
1088                    cache_control.flags |= CACHE_NO_TRANSFORM;
1089                } else if(token_compare(buf, token_start, token_end,
1090                                        "must-revalidate") ||
1091                          token_compare(buf, token_start, token_end,
1092                                        "must-validate")) { /* losers */
1093                    cache_control.flags |= CACHE_MUST_REVALIDATE;
1094                } else if(token_compare(buf, token_start, token_end, 
1095                                        "proxy-revalidate")) {
1096                    cache_control.flags |= CACHE_PROXY_REVALIDATE;
1097                } else if(token_compare(buf, token_start, token_end,
1098                                        "only-if-cached")) {
1099                    cache_control.flags |= CACHE_ONLY_IF_CACHED;
1100                } else if(token_compare(buf, token_start, token_end,
1101                                        "max-age") ||
1102                          token_compare(buf, token_start, token_end,
1103                                        "maxage") || /* losers */
1104                          token_compare(buf, token_start, token_end,
1105                                        "s-maxage") ||
1106                          token_compare(buf, token_start, token_end,
1107                                        "min-fresh")) {
1108                    parseCacheControl(buf, token_start, token_end,
1109                                      v_start, v_end,
1110                                      &cache_control.max_age);
1111                } else if(token_compare(buf, token_start, token_end,
1112                                        "max-stale")) {
1113                    parseCacheControl(buf, token_start, token_end,
1114                                      v_start, v_end,
1115                                      &cache_control.max_stale);
1116                } else {
1117                    do_log(L_WARN, "Unsupported Cache-Control directive ");
1118                    do_log_n(L_WARN, buf + token_start, 
1119                             (v_end >= 0 ? v_end : token_end) - token_start);
1120                    do_log(L_WARN, " -- ignored.\n");
1121                }
1122                if(end)
1123                    break;
1124                j = getNextTokenInList(buf, j, 
1125                                       &token_start, &token_end,
1126                                       &v_start, &v_end,
1127                                       &end);
1128            }
1129        } else if(name == atomContentRange) {
1130            if(!client) {
1131                j = parseContentRange(buf, value_start, 
1132                                      &content_range.from, &content_range.to, 
1133                                      &content_range.full_length);
1134                if(j < 0) {
1135                    do_log(L_ERROR, "Couldn't parse Content-Range: ");
1136                    do_log_n(L_ERROR, buf + value_start, 
1137                             value_end - value_start);
1138                    do_log(L_ERROR, "\n");
1139                    goto fail;
1140                }
1141            } else {
1142                do_log(L_ERROR, "Content-Range from client.\n");
1143                goto fail;
1144            }
1145        } else if(name == atomRange) {
1146            if(client) {
1147                j = parseRange(buf, value_start, &range.from, &range.to);
1148                if(j < 0) {
1149                    do_log(L_WARN, "Couldn't parse Range -- ignored.\n");
1150                    range.from = -1;
1151                    range.to = -1;
1152                }
1153            } else {
1154                do_log(L_WARN, "Range from server -- ignored\n");
1155            }
1156        } else if(name == atomXPolipoLocation) {
1157            if(location_return) {
1158                location = 
1159                    strdup_n(buf + value_start, value_end - value_start);
1160                if(location == NULL) {
1161                    do_log(L_ERROR, "Couldn't allocate location.\n");
1162                    goto fail;
1163                }
1164            }
1165        } else if(name == atomVia) {
1166            if(via_return) {
1167                AtomPtr new_via, full_via;
1168                new_via =
1169                    internAtomN(buf + value_start, value_end - value_start);
1170                if(new_via == NULL) {
1171                    do_log(L_ERROR, "Couldn't allocate via.\n");
1172                    goto fail;
1173                }
1174                if(via) {
1175                    full_via =
1176                        internAtomF("%s, %s", via->string, new_via->string);
1177                    releaseAtom(new_via);
1178                    if(full_via == NULL) {
1179                        do_log(L_ERROR, "Couldn't allocate via");
1180                        goto fail;
1181                    }
1182                    releaseAtom(via);
1183                    via = full_via;
1184                } else {
1185                    via = new_via;
1186                }
1187            }
1188        } else if(name == atomExpect) {
1189            if(expect_return) {
1190                expect = internAtomLowerN(buf + value_start, 
1191                                          value_end - value_start);
1192                if(expect == NULL) {
1193                    do_log(L_ERROR, "Couldn't allocate expect.\n");
1194                    goto fail;
1195                }
1196            }
1197        } else {
1198            if(!client && name == atomContentType) {
1199                if(token_compare(buf, value_start, value_end,
1200                                 "multipart/byteranges")) {
1201                    do_log(L_ERROR, 
1202                           "Server returned multipart/byteranges -- yuck!\n");
1203                    goto fail;
1204                }
1205            } 
1206            if(name == atomVary) {
1207                if(!token_compare(buf, value_start, value_end, "host") &&
1208                   !token_compare(buf, value_start, value_end, "*")) {
1209                    /* What other vary headers should be ignored? */
1210                    do_log(L_VARY, "Vary header present (");
1211                    do_log_n(L_VARY,
1212                             buf + value_start, value_end - value_start);
1213                    do_log(L_VARY, ").\n");
1214                }
1215                cache_control.flags |= CACHE_VARY;
1216            } else if(name == atomAuthorization) {
1217                cache_control.flags |= CACHE_AUTHORIZATION;
1218            } 
1219
1220            if(name == atomPragma) {
1221                /* Pragma is only defined for the client, and the only
1222                   standard value is no-cache (RFC 1945, 10.12).
1223                   However, we honour a Pragma: no-cache for both the client
1224                   and the server when there's no Cache-Control header.  In
1225                   all cases, we pass the Pragma header to the next hop. */
1226                if(!haveCacheControl) {
1227                    j = getNextTokenInList(buf, value_start,
1228                                           &token_start, &token_end, NULL, NULL,
1229                                           &end);
1230                    while(1) {
1231                        if(j < 0) {
1232                            do_log(L_WARN, "Couldn't parse Pragma.\n");
1233                            cache_control.flags |= CACHE_NO;
1234                            break;
1235                        }
1236                        if(token_compare(buf, token_start, token_end,
1237                                         "no-cache"))
1238                            cache_control.flags = CACHE_NO;
1239                        if(end)
1240                            break;
1241                        j = getNextTokenInList(buf, j, &token_start, &token_end,
1242                                               NULL, NULL, &end);
1243                    }
1244                }
1245            }
1246            if(!client &&
1247               (name == atomSetCookie || 
1248                name == atomCookie || name == atomCookie2))
1249                cache_control.flags |= CACHE_COOKIE;
1250
1251            if(hbuf) {
1252                if(name != atomConnection && name != atomHost &&
1253                   name != atomAcceptRange && name != atomTE &&
1254                   name != atomProxyAuthenticate &&
1255                   name != atomKeepAlive &&
1256                   (!hopToHop || !atomListMember(name, hopToHop)) &&
1257                   !atomListMember(name, censoredHeaders)) {
1258                    int h;
1259                    while(hbuf_length > hbuf_size - 2)
1260                        RESIZE_HBUF();
1261                    hbuf[hbuf_length++] = '\r';
1262                    hbuf[hbuf_length++] = '\n';
1263                    do {
1264                        h = snnprint_n(hbuf, hbuf_length, hbuf_size,
1265                                       buf + name_start, 
1266                                       value_end - name_start);
1267                        if(h < 0) RESIZE_HBUF();
1268                    } while(h < 0);
1269                    hbuf_length = h;
1270                }
1271            }
1272        }
1273        releaseAtom(name);
1274        name = NULL;
1275    }
1276
1277    if(headers_return) {
1278        AtomPtr pheaders = NULL; 
1279        pheaders = internAtomN(hbuf, hbuf_length);
1280        if(!pheaders)
1281            goto fail;
1282        *headers_return = pheaders;
1283    }
1284    if(hbuf != hbuf_small)
1285        free(hbuf);
1286    hbuf = NULL;
1287    hbuf_size = 0;
1288
1289    if(request)
1290        if(!persistent)
1291            request->flags &= ~REQUEST_PERSISTENT;
1292
1293    if(te != TE_IDENTITY) len = -1;
1294    if(len_return) *len_return = len;
1295    if(cache_control_return) *cache_control_return = cache_control;
1296    if(condition_return) {
1297        if(ims >= 0 || inms >= 0 || im || inm || ifrange) {
1298            condition = httpMakeCondition();
1299            if(condition) {
1300                condition->ims = ims;
1301                condition->inms = inms;
1302                condition->im = im;
1303                condition->inm = inm;
1304                condition->ifrange = ifrange;
1305            } else {
1306                do_log(L_ERROR, "Couldn't allocate condition.\n");
1307                if(im) free(im);
1308                if(inm) free(inm);
1309            }
1310        } else {
1311            condition = NULL;
1312        }
1313        *condition_return = condition;
1314    } else {
1315        assert(!im && !inm);
1316    }
1317            
1318    if(te_return) *te_return = te;
1319    if(date_return) *date_return = date;
1320    if(last_modified_return) *last_modified_return = last_modified;
1321    if(expires_return) *expires_return = expires;
1322    if(polipo_age_return) *polipo_age_return = polipo_age;
1323    if(polipo_access_return) *polipo_access_return = polipo_access;
1324    if(polipo_body_offset_return)
1325        *polipo_body_offset_return = polipo_body_offset;
1326    if(age_return) *age_return = age;
1327    if(etag_return)
1328        *etag_return = etag;
1329    else {
1330        if(etag) free(etag);
1331    }
1332    if(range_return) *range_return = range;
1333    if(content_range_return) *content_range_return = content_range;
1334    if(location_return) {
1335        *location_return = location;
1336    } else {
1337        if(location)
1338            free(location);
1339    }
1340    if(via_return)
1341        *via_return = via;
1342    else {
1343        if(via)
1344            releaseAtom(via);
1345    }
1346    if(expect_return)
1347        *expect_return = expect;
1348    else {
1349        if(expect)
1350            releaseAtom(expect);
1351    }
1352    if(auth_return)
1353        *auth_return = auth;
1354    else {
1355        if(auth)
1356            releaseAtom(auth);
1357    }
1358    if(hopToHop) destroyAtomList(hopToHop);
1359    return i;
1360
1361 fail:
1362    if(hbuf && hbuf != hbuf_small) free(hbuf);
1363    if(name) releaseAtom(name);
1364    if(etag) free(etag);
1365    if(location) free(location);
1366    if(via) releaseAtom(via);
1367    if(expect) releaseAtom(expect);
1368    if(auth) releaseAtom(auth);
1369    if(hopToHop) destroyAtomList(hopToHop);
1370        
1371    return -1;
1372#undef RESIZE_HBUF
1373}
1374
1375int
1376httpFindHeader(AtomPtr header, const char *headers, int hlen,
1377               int *value_begin_return, int *value_end_return)
1378{
1379    int len = header->length;
1380    int i = 0;
1381
1382    while(i + len + 1 < hlen) {
1383        if(headers[i + len] == ':' &&
1384           lwrcmp(headers + i, header->string, len) == 0) {
1385            int j = i + len + 1, k;
1386            while(j < hlen && headers[j] == ' ')
1387                j++;
1388            k = j;
1389            while(k < hlen && headers[k] != '\n' && headers[k] != '\r')
1390                k++;
1391            *value_begin_return = j;
1392            *value_end_return = k;
1393            return 1;
1394        } else {
1395            while(i < hlen && headers[i] != '\n' && headers[i] != '\r')
1396                i++;
1397            i++;
1398            if(i < hlen && headers[i] == '\n')
1399                i++;
1400        }
1401    }
1402    return 0;
1403}
1404
1405int
1406parseUrl(const char *url, int len,
1407         int *x_return, int *y_return, int *port_return, int *z_return)
1408{
1409    int x, y, z, port = -1, i = 0;
1410
1411    if(len >= 7 && lwrcmp(url, "http://", 7) == 0) {
1412        x = 7;
1413        if(x < len && url[x] == '[') {
1414            /* RFC 2732 */
1415            for(i = x + 1; i < len; i++) {
1416                if(url[i] == ']') {
1417                    i++;
1418                    break;
1419                }
1420                if((url[i] != ':') && !letter(url[i]) && !digit(url[i]))
1421                    break;
1422            }
1423        } else {
1424            for(i = x; i < len; i++)
1425                if(url[i] == ':' || url[i] == '/')
1426                    break;
1427        }
1428        y = i;
1429
1430        if(i < len && url[i] == ':') {
1431            int j;
1432            j = atoi_n(url, i + 1, len, &port);
1433            if(j < 0) {
1434                port = 80;
1435            } else {
1436                    i = j;
1437            }
1438        } else {
1439            port = 80;
1440        }
1441    } else {
1442        x = -1;
1443        y = -1;
1444    }
1445
1446    z = i;
1447
1448    *x_return = x;
1449    *y_return = y;
1450    *port_return = port;
1451    *z_return = z;
1452    return 0;
1453}
1454
1455int
1456urlIsLocal(const char *url, int len)
1457{
1458    return (len > 0 && url[0] == '/');
1459}
1460
1461int
1462urlIsSpecial(const char *url, int len)
1463{
1464    return (len >= 8 && memcmp(url, "/polipo/", 8) == 0);
1465}
1466
1467int
1468parseChunkSize(const char *restrict buf, int i, int end,
1469               int *chunk_size_return)
1470{
1471    int v, d;
1472    v = h2i(buf[i]);
1473    if(v < 0)
1474        return -1;
1475
1476    i++;
1477
1478    while(i < end) {
1479        d = h2i(buf[i]);
1480        if(d < 0)
1481            break;
1482        v = v * 16 + d;
1483        i++;
1484    }
1485
1486    while(i < end) {
1487        if(buf[i] == ' ' || buf[i] == '\t')
1488            i++;
1489        else
1490            break;
1491    }
1492
1493    if(i >= end - 1)
1494        return 0;
1495
1496    if(buf[i] != '\r' || buf[i + 1] != '\n')
1497        return -1;
1498
1499    i += 2;
1500
1501    if(v == 0) {
1502        if(i >= end - 1)
1503            return 0;
1504        if(buf[i] != '\r') {
1505            do_log(L_ERROR, "Trailers present!\n");
1506            return -1;
1507        }
1508        i++;
1509        if(buf[i] != '\n')
1510            return -1;
1511        i++;
1512    }
1513
1514    *chunk_size_return = v;
1515    return i;
1516}
1517
1518
1519int
1520checkVia(AtomPtr name, AtomPtr via)
1521{
1522    int i;
1523    char *v;
1524    if(via == NULL || via->length == 0)
1525        return 1;
1526
1527    v = via->string;
1528
1529    i = 0;
1530    while(i < via->length) {
1531        while(v[i] == ' ' || v[i] == '\t' || v[i] == ',' ||
1532              v[i] == '\r' || v[i] == '\n' ||
1533              digit(v[i]) || v[i] == '.')
1534            i++;
1535        if(i + name->length > via->length)
1536            break;
1537        if(memcmp(v + i, name->string, name->length) == 0) {
1538            char c = v[i + name->length];
1539            if(c == '\0' || c == ' ' || c == '\t' || c == ',' ||
1540               c == '\r' || c == '\n')
1541                return 0;
1542        }
1543        i++;
1544        while(letter(v[i]) || digit(v[i]) || v[i] == '.')
1545            i++;
1546    }
1547    return 1;
1548}