1 /*- 2 * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * The following copyright applies to the base64 code: 34 * 35 *- 36 * Copyright 1997 Massachusetts Institute of Technology 37 * 38 * Permission to use, copy, modify, and distribute this software and 39 * its documentation for any purpose and without fee is hereby 40 * granted, provided that both the above copyright notice and this 41 * permission notice appear in all copies, that both the above 42 * copyright notice and this permission notice appear in all 43 * supporting documentation, and that the name of M.I.T. not be used 44 * in advertising or publicity pertaining to distribution of the 45 * software without specific, written prior permission. M.I.T. makes 46 * no representations about the suitability of this software for any 47 * purpose. It is provided "as is" without express or implied 48 * warranty. 49 * 50 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 51 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 52 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 53 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 54 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 57 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 58 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 59 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 60 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 #include <sys/param.h> 65 #include <sys/socket.h> 66 67 #include <ctype.h> 68 #include <err.h> 69 #include <errno.h> 70 #include <locale.h> 71 #include <netdb.h> 72 #include <stdarg.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #include <time.h> 77 #include <unistd.h> 78 79 #include "fetch.h" 80 #include "common.h" 81 #include "httperr.h" 82 83 extern char *__progname; /* XXX not portable */ 84 85 /* Maximum number of redirects to follow */ 86 #define MAX_REDIRECT 5 87 88 /* Symbolic names for reply codes we care about */ 89 #define HTTP_OK 200 90 #define HTTP_PARTIAL 206 91 #define HTTP_MOVED_PERM 301 92 #define HTTP_MOVED_TEMP 302 93 #define HTTP_SEE_OTHER 303 94 #define HTTP_NEED_AUTH 401 95 #define HTTP_NEED_PROXY_AUTH 407 96 #define HTTP_PROTOCOL_ERROR 999 97 98 #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ 99 || (xyz) == HTTP_MOVED_TEMP \ 100 || (xyz) == HTTP_SEE_OTHER) 101 102 103 104 /***************************************************************************** 105 * I/O functions for decoding chunked streams 106 */ 107 108 struct cookie 109 { 110 int fd; 111 char *buf; 112 size_t b_size; 113 ssize_t b_len; 114 int b_pos; 115 int eof; 116 int error; 117 size_t chunksize; 118 #ifndef NDEBUG 119 size_t total; 120 #endif 121 }; 122 123 /* 124 * Get next chunk header 125 */ 126 static int 127 _http_new_chunk(struct cookie *c) 128 { 129 char *p; 130 131 if (_fetch_getln(c->fd, &c->buf, &c->b_size, &c->b_len) == -1) 132 return -1; 133 134 if (c->b_len < 2 || !ishexnumber(*c->buf)) 135 return -1; 136 137 for (p = c->buf; !isspace(*p) && *p != ';' && p < c->buf + c->b_len; ++p) 138 if (!ishexnumber(*p)) 139 return -1; 140 else if (isdigit(*p)) 141 c->chunksize = c->chunksize * 16 + *p - '0'; 142 else 143 c->chunksize = c->chunksize * 16 + 10 + tolower(*p) - 'a'; 144 145 #ifndef NDEBUG 146 if (fetchDebug) { 147 c->total += c->chunksize; 148 if (c->chunksize == 0) 149 fprintf(stderr, "\033[1m_http_fillbuf(): " 150 "end of last chunk\033[m\n"); 151 else 152 fprintf(stderr, "\033[1m_http_fillbuf(): " 153 "new chunk: %lu (%lu)\033[m\n", 154 (unsigned long)c->chunksize, (unsigned long)c->total); 155 } 156 #endif 157 158 return c->chunksize; 159 } 160 161 /* 162 * Fill the input buffer, do chunk decoding on the fly 163 */ 164 static int 165 _http_fillbuf(struct cookie *c) 166 { 167 if (c->error) 168 return -1; 169 if (c->eof) 170 return 0; 171 172 if (c->chunksize == 0) { 173 switch (_http_new_chunk(c)) { 174 case -1: 175 c->error = 1; 176 return -1; 177 case 0: 178 c->eof = 1; 179 return 0; 180 } 181 } 182 183 if (c->b_size < c->chunksize) { 184 char *tmp; 185 186 if ((tmp = realloc(c->buf, c->chunksize)) == NULL) 187 return -1; 188 c->buf = tmp; 189 c->b_size = c->chunksize; 190 } 191 192 if ((c->b_len = read(c->fd, c->buf, c->chunksize)) == -1) 193 return -1; 194 c->chunksize -= c->b_len; 195 196 if (c->chunksize == 0) { 197 char endl[2]; 198 read(c->fd, endl, 2); 199 } 200 201 c->b_pos = 0; 202 203 return c->b_len; 204 } 205 206 /* 207 * Read function 208 */ 209 static int 210 _http_readfn(void *v, char *buf, int len) 211 { 212 struct cookie *c = (struct cookie *)v; 213 int l, pos; 214 215 if (c->error) 216 return -1; 217 if (c->eof) 218 return 0; 219 220 for (pos = 0; len > 0; pos += l, len -= l) { 221 /* empty buffer */ 222 if (!c->buf || c->b_pos == c->b_len) 223 if (_http_fillbuf(c) < 1) 224 break; 225 l = c->b_len - c->b_pos; 226 if (len < l) 227 l = len; 228 bcopy(c->buf + c->b_pos, buf + pos, l); 229 c->b_pos += l; 230 } 231 232 if (!pos && c->error) 233 return -1; 234 return pos; 235 } 236 237 /* 238 * Write function 239 */ 240 static int 241 _http_writefn(void *v, const char *buf, int len) 242 { 243 struct cookie *c = (struct cookie *)v; 244 245 return write(c->fd, buf, len); 246 } 247 248 /* 249 * Close function 250 */ 251 static int 252 _http_closefn(void *v) 253 { 254 struct cookie *c = (struct cookie *)v; 255 int r; 256 257 r = close(c->fd); 258 if (c->buf) 259 free(c->buf); 260 free(c); 261 return r; 262 } 263 264 /* 265 * Wrap a file descriptor up 266 */ 267 static FILE * 268 _http_funopen(int fd) 269 { 270 struct cookie *c; 271 FILE *f; 272 273 if ((c = calloc(1, sizeof *c)) == NULL) { 274 _fetch_syserr(); 275 return NULL; 276 } 277 c->fd = fd; 278 if (!(f = funopen(c, _http_readfn, _http_writefn, NULL, _http_closefn))) { 279 _fetch_syserr(); 280 free(c); 281 return NULL; 282 } 283 return f; 284 } 285 286 287 /***************************************************************************** 288 * Helper functions for talking to the server and parsing its replies 289 */ 290 291 /* Header types */ 292 typedef enum { 293 hdr_syserror = -2, 294 hdr_error = -1, 295 hdr_end = 0, 296 hdr_unknown = 1, 297 hdr_content_length, 298 hdr_content_range, 299 hdr_last_modified, 300 hdr_location, 301 hdr_transfer_encoding, 302 hdr_www_authenticate 303 } hdr_t; 304 305 /* Names of interesting headers */ 306 static struct { 307 hdr_t num; 308 const char *name; 309 } hdr_names[] = { 310 { hdr_content_length, "Content-Length" }, 311 { hdr_content_range, "Content-Range" }, 312 { hdr_last_modified, "Last-Modified" }, 313 { hdr_location, "Location" }, 314 { hdr_transfer_encoding, "Transfer-Encoding" }, 315 { hdr_www_authenticate, "WWW-Authenticate" }, 316 { hdr_unknown, NULL }, 317 }; 318 319 static char *reply_buf; 320 static size_t reply_size; 321 static size_t reply_length; 322 323 /* 324 * Send a formatted line; optionally echo to terminal 325 */ 326 static int 327 _http_cmd(int fd, const char *fmt, ...) 328 { 329 va_list ap; 330 size_t len; 331 char *msg; 332 int r; 333 334 va_start(ap, fmt); 335 len = vasprintf(&msg, fmt, ap); 336 va_end(ap); 337 338 if (msg == NULL) { 339 errno = ENOMEM; 340 _fetch_syserr(); 341 return -1; 342 } 343 344 r = _fetch_putln(fd, msg, len); 345 free(msg); 346 347 if (r == -1) { 348 _fetch_syserr(); 349 return -1; 350 } 351 352 return 0; 353 } 354 355 /* 356 * Get and parse status line 357 */ 358 static int 359 _http_get_reply(int fd) 360 { 361 char *p; 362 363 if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1) 364 return -1; 365 /* 366 * A valid status line looks like "HTTP/m.n xyz reason" where m 367 * and n are the major and minor protocol version numbers and xyz 368 * is the reply code. 369 * Unfortunately, there are servers out there (NCSA 1.5.1, to name 370 * just one) that do not send a version number, so we can't rely 371 * on finding one, but if we do, insist on it being 1.0 or 1.1. 372 * We don't care about the reason phrase. 373 */ 374 if (strncmp(reply_buf, "HTTP", 4) != 0) 375 return HTTP_PROTOCOL_ERROR; 376 p = reply_buf + 4; 377 if (*p == '/') { 378 if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1')) 379 return HTTP_PROTOCOL_ERROR; 380 p += 4; 381 } 382 if (*p != ' ' 383 || !isdigit(p[1]) 384 || !isdigit(p[2]) 385 || !isdigit(p[3])) 386 return HTTP_PROTOCOL_ERROR; 387 388 return ((p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0')); 389 } 390 391 /* 392 * Check a header; if the type matches the given string, return a 393 * pointer to the beginning of the value. 394 */ 395 static const char * 396 _http_match(const char *str, const char *hdr) 397 { 398 while (*str && *hdr && tolower(*str++) == tolower(*hdr++)) 399 /* nothing */; 400 if (*str || *hdr != ':') 401 return NULL; 402 while (*hdr && isspace(*++hdr)) 403 /* nothing */; 404 return hdr; 405 } 406 407 /* 408 * Get the next header and return the appropriate symbolic code. 409 */ 410 static hdr_t 411 _http_next_header(int fd, const char **p) 412 { 413 int i; 414 415 if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1) 416 return hdr_syserror; 417 while (reply_length && isspace(reply_buf[reply_length-1])) 418 reply_length--; 419 reply_buf[reply_length] = 0; 420 if (reply_length == 0) 421 return hdr_end; 422 /* 423 * We could check for malformed headers but we don't really care. 424 * A valid header starts with a token immediately followed by a 425 * colon; a token is any sequence of non-control, non-whitespace 426 * characters except "()<>@,;:\\\"{}". 427 */ 428 for (i = 0; hdr_names[i].num != hdr_unknown; i++) 429 if ((*p = _http_match(hdr_names[i].name, reply_buf)) != NULL) 430 return hdr_names[i].num; 431 return hdr_unknown; 432 } 433 434 /* 435 * Parse a last-modified header 436 */ 437 static int 438 _http_parse_mtime(const char *p, time_t *mtime) 439 { 440 char locale[64], *r; 441 struct tm tm; 442 443 strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale); 444 setlocale(LC_TIME, "C"); 445 r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 446 /* XXX should add support for date-2 and date-3 */ 447 setlocale(LC_TIME, locale); 448 if (r == NULL) 449 return -1; 450 DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 451 "%02d:%02d:%02d\033[m]\n", 452 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 453 tm.tm_hour, tm.tm_min, tm.tm_sec)); 454 *mtime = timegm(&tm); 455 return 0; 456 } 457 458 /* 459 * Parse a content-length header 460 */ 461 static int 462 _http_parse_length(const char *p, off_t *length) 463 { 464 off_t len; 465 466 for (len = 0; *p && isdigit(*p); ++p) 467 len = len * 10 + (*p - '0'); 468 if (*p) 469 return -1; 470 DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", 471 (long long)len)); 472 *length = len; 473 return 0; 474 } 475 476 /* 477 * Parse a content-range header 478 */ 479 static int 480 _http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size) 481 { 482 off_t first, last, len; 483 484 if (strncasecmp(p, "bytes ", 6) != 0) 485 return -1; 486 for (first = 0, p += 6; *p && isdigit(*p); ++p) 487 first = first * 10 + *p - '0'; 488 if (*p != '-') 489 return -1; 490 for (last = 0, ++p; *p && isdigit(*p); ++p) 491 last = last * 10 + *p - '0'; 492 if (first > last || *p != '/') 493 return -1; 494 for (len = 0, ++p; *p && isdigit(*p); ++p) 495 len = len * 10 + *p - '0'; 496 if (*p || len < last - first + 1) 497 return -1; 498 DEBUG(fprintf(stderr, "content range: [\033[1m%lld-%lld/%lld\033[m]\n", 499 (long long)first, (long long)last, (long long)len)); 500 *offset = first; 501 *length = last - first + 1; 502 *size = len; 503 return 0; 504 } 505 506 507 /***************************************************************************** 508 * Helper functions for authorization 509 */ 510 511 /* 512 * Base64 encoding 513 */ 514 static char * 515 _http_base64(char *src) 516 { 517 static const char base64[] = 518 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 519 "abcdefghijklmnopqrstuvwxyz" 520 "0123456789+/"; 521 char *str, *dst; 522 size_t l; 523 int t, r; 524 525 l = strlen(src); 526 if ((str = malloc(((l + 2) / 3) * 4)) == NULL) 527 return NULL; 528 dst = str; 529 r = 0; 530 531 while (l >= 3) { 532 t = (src[0] << 16) | (src[1] << 8) | src[2]; 533 dst[0] = base64[(t >> 18) & 0x3f]; 534 dst[1] = base64[(t >> 12) & 0x3f]; 535 dst[2] = base64[(t >> 6) & 0x3f]; 536 dst[3] = base64[(t >> 0) & 0x3f]; 537 src += 3; l -= 3; 538 dst += 4; r += 4; 539 } 540 541 switch (l) { 542 case 2: 543 t = (src[0] << 16) | (src[1] << 8); 544 dst[0] = base64[(t >> 18) & 0x3f]; 545 dst[1] = base64[(t >> 12) & 0x3f]; 546 dst[2] = base64[(t >> 6) & 0x3f]; 547 dst[3] = '='; 548 dst += 4; 549 r += 4; 550 break; 551 case 1: 552 t = src[0] << 16; 553 dst[0] = base64[(t >> 18) & 0x3f]; 554 dst[1] = base64[(t >> 12) & 0x3f]; 555 dst[2] = dst[3] = '='; 556 dst += 4; 557 r += 4; 558 break; 559 case 0: 560 break; 561 } 562 563 *dst = 0; 564 return str; 565 } 566 567 /* 568 * Encode username and password 569 */ 570 static int 571 _http_basic_auth(int fd, const char *hdr, const char *usr, const char *pwd) 572 { 573 char *upw, *auth; 574 int r; 575 576 DEBUG(fprintf(stderr, "usr: [\033[1m%s\033[m]\n", usr)); 577 DEBUG(fprintf(stderr, "pwd: [\033[1m%s\033[m]\n", pwd)); 578 if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 579 return -1; 580 auth = _http_base64(upw); 581 free(upw); 582 if (auth == NULL) 583 return -1; 584 r = _http_cmd(fd, "%s: Basic %s", hdr, auth); 585 free(auth); 586 return r; 587 } 588 589 /* 590 * Send an authorization header 591 */ 592 static int 593 _http_authorize(int fd, const char *hdr, const char *p) 594 { 595 /* basic authorization */ 596 if (strncasecmp(p, "basic:", 6) == 0) { 597 char *user, *pwd, *str; 598 int r; 599 600 /* skip realm */ 601 for (p += 6; *p && *p != ':'; ++p) 602 /* nothing */ ; 603 if (!*p || strchr(++p, ':') == NULL) 604 return -1; 605 if ((str = strdup(p)) == NULL) 606 return -1; /* XXX */ 607 user = str; 608 pwd = strchr(str, ':'); 609 *pwd++ = '\0'; 610 r = _http_basic_auth(fd, hdr, user, pwd); 611 free(str); 612 return r; 613 } 614 return -1; 615 } 616 617 618 /***************************************************************************** 619 * Helper functions for connecting to a server or proxy 620 */ 621 622 /* 623 * Connect to the correct HTTP server or proxy. 624 */ 625 static int 626 _http_connect(struct url *URL, struct url *purl, const char *flags) 627 { 628 int verbose; 629 int af, fd; 630 631 #ifdef INET6 632 af = AF_UNSPEC; 633 #else 634 af = AF_INET; 635 #endif 636 637 verbose = CHECK_FLAG('v'); 638 if (CHECK_FLAG('4')) 639 af = AF_INET; 640 #ifdef INET6 641 else if (CHECK_FLAG('6')) 642 af = AF_INET6; 643 #endif 644 645 if (purl) { 646 URL = purl; 647 } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) { 648 /* can't talk http to an ftp server */ 649 /* XXX should set an error code */ 650 return -1; 651 } 652 653 if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1) 654 /* _fetch_connect() has already set an error code */ 655 return -1; 656 return fd; 657 } 658 659 static struct url * 660 _http_get_proxy(void) 661 { 662 struct url *purl; 663 char *p; 664 665 if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 666 (purl = fetchParseURL(p))) { 667 if (!*purl->scheme) 668 strcpy(purl->scheme, SCHEME_HTTP); 669 if (!purl->port) 670 purl->port = _fetch_default_proxy_port(purl->scheme); 671 if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 672 return purl; 673 fetchFreeURL(purl); 674 } 675 return NULL; 676 } 677 678 679 /***************************************************************************** 680 * Core 681 */ 682 683 /* 684 * Send a request and process the reply 685 */ 686 FILE * 687 _http_request(struct url *URL, const char *op, struct url_stat *us, 688 struct url *purl, const char *flags) 689 { 690 struct url *url, *new; 691 int chunked, direct, need_auth, noredirect, verbose; 692 int code, fd, i, n; 693 off_t offset, clength, length, size; 694 time_t mtime; 695 const char *p; 696 FILE *f; 697 hdr_t h; 698 char *host; 699 #ifdef INET6 700 char hbuf[MAXHOSTNAMELEN + 1]; 701 #endif 702 703 direct = CHECK_FLAG('d'); 704 noredirect = CHECK_FLAG('A'); 705 verbose = CHECK_FLAG('v'); 706 707 if (direct && purl) { 708 fetchFreeURL(purl); 709 purl = NULL; 710 } 711 712 /* try the provided URL first */ 713 url = URL; 714 715 /* if the A flag is set, we only get one try */ 716 n = noredirect ? 1 : MAX_REDIRECT; 717 i = 0; 718 719 need_auth = 0; 720 do { 721 new = NULL; 722 chunked = 0; 723 offset = 0; 724 clength = -1; 725 length = -1; 726 size = -1; 727 mtime = 0; 728 729 /* check port */ 730 if (!url->port) 731 url->port = _fetch_default_port(url->scheme); 732 733 /* were we redirected to an FTP URL? */ 734 if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) { 735 if (strcmp(op, "GET") == 0) 736 return _ftp_request(url, "RETR", us, purl, flags); 737 else if (strcmp(op, "HEAD") == 0) 738 return _ftp_request(url, "STAT", us, purl, flags); 739 } 740 741 /* connect to server or proxy */ 742 if ((fd = _http_connect(url, purl, flags)) == -1) 743 goto ouch; 744 745 host = url->host; 746 #ifdef INET6 747 if (strchr(url->host, ':')) { 748 snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); 749 host = hbuf; 750 } 751 #endif 752 753 /* send request */ 754 if (verbose) 755 _fetch_info("requesting %s://%s:%d%s", 756 url->scheme, host, url->port, url->doc); 757 if (purl) { 758 _http_cmd(fd, "%s %s://%s:%d%s HTTP/1.1", 759 op, url->scheme, host, url->port, url->doc); 760 } else { 761 _http_cmd(fd, "%s %s HTTP/1.1", 762 op, url->doc); 763 } 764 765 /* virtual host */ 766 if (url->port == _fetch_default_port(url->scheme)) 767 _http_cmd(fd, "Host: %s", host); 768 else 769 _http_cmd(fd, "Host: %s:%d", host, url->port); 770 771 /* proxy authorization */ 772 if (purl) { 773 if (*purl->user || *purl->pwd) 774 _http_basic_auth(fd, "Proxy-Authorization", 775 purl->user, purl->pwd); 776 else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') 777 _http_authorize(fd, "Proxy-Authorization", p); 778 } 779 780 /* server authorization */ 781 if (need_auth || *url->user || *url->pwd) { 782 if (*url->user || *url->pwd) 783 _http_basic_auth(fd, "Authorization", url->user, url->pwd); 784 else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') 785 _http_authorize(fd, "Authorization", p); 786 else if (fetchAuthMethod && fetchAuthMethod(url) == 0) { 787 _http_basic_auth(fd, "Authorization", url->user, url->pwd); 788 } else { 789 _http_seterr(HTTP_NEED_AUTH); 790 goto ouch; 791 } 792 } 793 794 /* other headers */ 795 if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') 796 _http_cmd(fd, "User-Agent: %s", p); 797 else 798 _http_cmd(fd, "User-Agent: %s " _LIBFETCH_VER, __progname); 799 if (url->offset) 800 _http_cmd(fd, "Range: bytes=%lld-", (long long)url->offset); 801 _http_cmd(fd, "Connection: close"); 802 _http_cmd(fd, ""); 803 804 /* get reply */ 805 switch ((code = _http_get_reply(fd))) { 806 case HTTP_OK: 807 case HTTP_PARTIAL: 808 /* fine */ 809 break; 810 case HTTP_MOVED_PERM: 811 case HTTP_MOVED_TEMP: 812 case HTTP_SEE_OTHER: 813 /* 814 * Not so fine, but we still have to read the headers to 815 * get the new location. 816 */ 817 break; 818 case HTTP_NEED_AUTH: 819 if (need_auth) { 820 /* 821 * We already sent out authorization code, so there's 822 * nothing more we can do. 823 */ 824 _http_seterr(code); 825 goto ouch; 826 } 827 /* try again, but send the password this time */ 828 if (verbose) 829 _fetch_info("server requires authorization"); 830 break; 831 case HTTP_NEED_PROXY_AUTH: 832 /* 833 * If we're talking to a proxy, we already sent our proxy 834 * authorization code, so there's nothing more we can do. 835 */ 836 _http_seterr(code); 837 goto ouch; 838 case HTTP_PROTOCOL_ERROR: 839 /* fall through */ 840 case -1: 841 _fetch_syserr(); 842 goto ouch; 843 default: 844 _http_seterr(code); 845 goto ouch; 846 } 847 848 /* get headers */ 849 do { 850 switch ((h = _http_next_header(fd, &p))) { 851 case hdr_syserror: 852 _fetch_syserr(); 853 goto ouch; 854 case hdr_error: 855 _http_seterr(HTTP_PROTOCOL_ERROR); 856 goto ouch; 857 case hdr_content_length: 858 _http_parse_length(p, &clength); 859 break; 860 case hdr_content_range: 861 _http_parse_range(p, &offset, &length, &size); 862 break; 863 case hdr_last_modified: 864 _http_parse_mtime(p, &mtime); 865 break; 866 case hdr_location: 867 if (!HTTP_REDIRECT(code)) 868 break; 869 if (new) 870 free(new); 871 if (verbose) 872 _fetch_info("%d redirect to %s", code, p); 873 if (*p == '/') 874 /* absolute path */ 875 new = fetchMakeURL(url->scheme, url->host, url->port, p, 876 url->user, url->pwd); 877 else 878 new = fetchParseURL(p); 879 if (new == NULL) { 880 /* XXX should set an error code */ 881 DEBUG(fprintf(stderr, "failed to parse new URL\n")); 882 goto ouch; 883 } 884 if (!*new->user && !*new->pwd) { 885 strcpy(new->user, url->user); 886 strcpy(new->pwd, url->pwd); 887 } 888 new->offset = url->offset; 889 new->length = url->length; 890 break; 891 case hdr_transfer_encoding: 892 /* XXX weak test*/ 893 chunked = (strcasecmp(p, "chunked") == 0); 894 break; 895 case hdr_www_authenticate: 896 if (code != HTTP_NEED_AUTH) 897 break; 898 /* if we were smarter, we'd check the method and realm */ 899 break; 900 case hdr_end: 901 /* fall through */ 902 case hdr_unknown: 903 /* ignore */ 904 break; 905 } 906 } while (h > hdr_end); 907 908 /* we have a hit */ 909 if (code == HTTP_OK || code == HTTP_PARTIAL) 910 break; 911 912 /* we need to provide authentication */ 913 if (code == HTTP_NEED_AUTH) { 914 need_auth = 1; 915 close(fd); 916 fd = -1; 917 continue; 918 } 919 920 /* all other cases: we got a redirect */ 921 need_auth = 0; 922 close(fd); 923 fd = -1; 924 if (!new) { 925 DEBUG(fprintf(stderr, "redirect with no new location\n")); 926 break; 927 } 928 if (url != URL) 929 fetchFreeURL(url); 930 url = new; 931 } while (++i < n); 932 933 /* we failed, or ran out of retries */ 934 if (fd == -1) { 935 _http_seterr(code); 936 goto ouch; 937 } 938 939 DEBUG(fprintf(stderr, "offset %lld, length %lld," 940 " size %lld, clength %lld\n", 941 (long long)offset, (long long)length, 942 (long long)size, (long long)clength)); 943 944 /* check for inconsistencies */ 945 if (clength != -1 && length != -1 && clength != length) { 946 _http_seterr(HTTP_PROTOCOL_ERROR); 947 goto ouch; 948 } 949 if (clength == -1) 950 clength = length; 951 if (clength != -1) 952 length = offset + clength; 953 if (length != -1 && size != -1 && length != size) { 954 _http_seterr(HTTP_PROTOCOL_ERROR); 955 goto ouch; 956 } 957 if (size == -1) 958 size = length; 959 960 /* fill in stats */ 961 if (us) { 962 us->size = size; 963 us->atime = us->mtime = mtime; 964 } 965 966 /* too far? */ 967 if (offset > URL->offset) { 968 _http_seterr(HTTP_PROTOCOL_ERROR); 969 goto ouch; 970 } 971 972 /* report back real offset and size */ 973 URL->offset = offset; 974 URL->length = clength; 975 976 /* wrap it up in a FILE */ 977 if ((f = chunked ? _http_funopen(fd) : fdopen(fd, "r")) == NULL) { 978 _fetch_syserr(); 979 goto ouch; 980 } 981 982 if (url != URL) 983 fetchFreeURL(url); 984 if (purl) 985 fetchFreeURL(purl); 986 987 return f; 988 989 ouch: 990 if (url != URL) 991 fetchFreeURL(url); 992 if (purl) 993 fetchFreeURL(purl); 994 if (fd != -1) 995 close(fd); 996 return NULL; 997 } 998 999 1000 /***************************************************************************** 1001 * Entry points 1002 */ 1003 1004 /* 1005 * Retrieve and stat a file by HTTP 1006 */ 1007 FILE * 1008 fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags) 1009 { 1010 return _http_request(URL, "GET", us, _http_get_proxy(), flags); 1011 } 1012 1013 /* 1014 * Retrieve a file by HTTP 1015 */ 1016 FILE * 1017 fetchGetHTTP(struct url *URL, const char *flags) 1018 { 1019 return fetchXGetHTTP(URL, NULL, flags); 1020 } 1021 1022 /* 1023 * Store a file by HTTP 1024 */ 1025 FILE * 1026 fetchPutHTTP(struct url *URL __unused, const char *flags __unused) 1027 { 1028 warnx("fetchPutHTTP(): not implemented"); 1029 return NULL; 1030 } 1031 1032 /* 1033 * Get an HTTP document's metadata 1034 */ 1035 int 1036 fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags) 1037 { 1038 FILE *f; 1039 1040 if ((f = _http_request(URL, "HEAD", us, _http_get_proxy(), flags)) == NULL) 1041 return -1; 1042 fclose(f); 1043 return 0; 1044 } 1045 1046 /* 1047 * List a directory 1048 */ 1049 struct url_ent * 1050 fetchListHTTP(struct url *url __unused, const char *flags __unused) 1051 { 1052 warnx("fetchListHTTP(): not implemented"); 1053 return NULL; 1054 } 1055