http.c revision 184222
118316Swollman/*- 218316Swollman * Copyright (c) 2000-2004 Dag-Erling Co�dan Sm�rgrav 318316Swollman * All rights reserved. 418316Swollman * 518316Swollman * Redistribution and use in source and binary forms, with or without 618316Swollman * modification, are permitted provided that the following conditions 718316Swollman * are met: 818316Swollman * 1. Redistributions of source code must retain the above copyright 918316Swollman * notice, this list of conditions and the following disclaimer 1018316Swollman * in this position and unchanged. 1118316Swollman * 2. Redistributions in binary form must reproduce the above copyright 1218316Swollman * notice, this list of conditions and the following disclaimer in the 1318316Swollman * documentation and/or other materials provided with the distribution. 1418316Swollman * 3. The name of the author may not be used to endorse or promote products 1518316Swollman * derived from this software without specific prior written permission. 1618316Swollman * 1718316Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1818316Swollman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1918316Swollman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2018316Swollman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2118316Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2218316Swollman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2318316Swollman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2418316Swollman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2518316Swollman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2618316Swollman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2718316Swollman */ 2846303Smarkm 2950476Speter#include <sys/cdefs.h> 3018316Swollman__FBSDID("$FreeBSD: head/lib/libfetch/http.c 184222 2008-10-24 07:56:01Z ru $"); 3118316Swollman 3218316Swollman/* 3318316Swollman * The following copyright applies to the base64 code: 3418316Swollman * 3518316Swollman *- 3646303Smarkm * Copyright 1997 Massachusetts Institute of Technology 3718316Swollman * 3818316Swollman * Permission to use, copy, modify, and distribute this software and 39126250Sbms * its documentation for any purpose and without fee is hereby 4046303Smarkm * granted, provided that both the above copyright notice and this 41126250Sbms * permission notice appear in all copies, that both the above 42126250Sbms * copyright notice and this permission notice appear in all 43126250Sbms * supporting documentation, and that the name of M.I.T. not be used 44126250Sbms * in advertising or publicity pertaining to distribution of the 45126250Sbms * software without specific, written prior permission. M.I.T. makes 4646303Smarkm * no representations about the suitability of this software for any 4746303Smarkm * purpose. It is provided "as is" without express or implied 4846303Smarkm * warranty. 4918316Swollman * 50102231Strhodes * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 5118316Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 5218316Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 5318316Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 5420339Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 5581604Speter * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 5646303Smarkm * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 57118582Simp * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 58118582Simp * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 59190715Sphk * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 6018316Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6118316Swollman * SUCH DAMAGE. 6246303Smarkm */ 6318316Swollman 6418316Swollman#include <sys/param.h> 6519880Swollman#include <sys/socket.h> 6619880Swollman 6719880Swollman#include <ctype.h> 6819880Swollman#include <err.h> 6919880Swollman#include <errno.h> 7019880Swollman#include <locale.h> 7119880Swollman#include <netdb.h> 7219880Swollman#include <stdarg.h> 7319880Swollman#include <stdio.h> 7419880Swollman#include <stdlib.h> 7519880Swollman#include <string.h> 7619880Swollman#include <time.h> 7719880Swollman#include <unistd.h> 7819880Swollman 7919880Swollman#include <netinet/in.h> 8019880Swollman#include <netinet/tcp.h> 8119880Swollman 8219880Swollman#include "fetch.h" 8319880Swollman#include "common.h" 8419880Swollman#include "httperr.h" 8519880Swollman 8619880Swollman/* Maximum number of redirects to follow */ 8719880Swollman#define MAX_REDIRECT 5 8819880Swollman 8919880Swollman/* Symbolic names for reply codes we care about */ 9019880Swollman#define HTTP_OK 200 9119880Swollman#define HTTP_PARTIAL 206 9219880Swollman#define HTTP_MOVED_PERM 301 9319880Swollman#define HTTP_MOVED_TEMP 302 9419880Swollman#define HTTP_SEE_OTHER 303 9519880Swollman#define HTTP_TEMP_REDIRECT 307 9619880Swollman#define HTTP_NEED_AUTH 401 9719880Swollman#define HTTP_NEED_PROXY_AUTH 407 9819880Swollman#define HTTP_BAD_RANGE 416 9919880Swollman#define HTTP_PROTOCOL_ERROR 999 10019880Swollman 10119880Swollman#define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ 10219880Swollman || (xyz) == HTTP_MOVED_TEMP \ 10319880Swollman || (xyz) == HTTP_TEMP_REDIRECT \ 10419880Swollman || (xyz) == HTTP_SEE_OTHER) 10519880Swollman 10619880Swollman#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) 10719880Swollman 10819880Swollman 10919880Swollman/***************************************************************************** 11019880Swollman * I/O functions for decoding chunked streams 11119880Swollman */ 11219880Swollman 11319880Swollmanstruct httpio 11419880Swollman{ 11519880Swollman conn_t *conn; /* connection */ 11619880Swollman int chunked; /* chunked mode */ 11719880Swollman char *buf; /* chunk buffer */ 11818316Swollman size_t bufsize; /* size of chunk buffer */ 11918316Swollman ssize_t buflen; /* amount of data currently in buffer */ 12018316Swollman int bufpos; /* current read offset in buffer */ 12118316Swollman int eof; /* end-of-file flag */ 12218316Swollman int error; /* error flag */ 12318316Swollman size_t chunksize; /* remaining size of current chunk */ 12418316Swollman#ifndef NDEBUG 12518316Swollman size_t total; 12618316Swollman#endif 12718316Swollman}; 12818316Swollman 12918316Swollman/* 13018316Swollman * Get next chunk header 13118316Swollman */ 13218316Swollmanstatic int 13318316Swollmanhttp_new_chunk(struct httpio *io) 13418316Swollman{ 13518316Swollman char *p; 13618316Swollman 13718316Swollman if (fetch_getln(io->conn) == -1) 13818316Swollman return (-1); 13946303Smarkm 14018316Swollman if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf)) 14118316Swollman return (-1); 14218316Swollman 14318316Swollman for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) { 14418316Swollman if (*p == ';') 14518316Swollman break; 14618316Swollman if (!isxdigit((unsigned char)*p)) 14718316Swollman return (-1); 14818316Swollman if (isdigit((unsigned char)*p)) { 14918316Swollman io->chunksize = io->chunksize * 16 + 15018316Swollman *p - '0'; 15118316Swollman } else { 15218316Swollman io->chunksize = io->chunksize * 16 + 15318316Swollman 10 + tolower((unsigned char)*p) - 'a'; 15446303Smarkm } 15518316Swollman } 15618316Swollman 15718316Swollman#ifndef NDEBUG 15818316Swollman if (fetchDebug) { 15918316Swollman io->total += io->chunksize; 16018316Swollman if (io->chunksize == 0) 16118316Swollman fprintf(stderr, "%s(): end of last chunk\n", __func__); 16218316Swollman else 16318316Swollman fprintf(stderr, "%s(): new chunk: %lu (%lu)\n", 16418316Swollman __func__, (unsigned long)io->chunksize, 16520339Swollman (unsigned long)io->total); 16620339Swollman } 16718316Swollman#endif 16818316Swollman 16918316Swollman return (io->chunksize); 17020339Swollman} 17120339Swollman 17218316Swollman/* 17320339Swollman * Grow the input buffer to at least len bytes 17418316Swollman */ 17518316Swollmanstatic inline int 17618316Swollmanhttp_growbuf(struct httpio *io, size_t len) 17718316Swollman{ 17818316Swollman char *tmp; 17946303Smarkm 18018316Swollman if (io->bufsize >= len) 18118316Swollman return (0); 18218316Swollman 18318316Swollman if ((tmp = realloc(io->buf, len)) == NULL) 18418316Swollman return (-1); 18518316Swollman io->buf = tmp; 18618316Swollman io->bufsize = len; 187126250Sbms return (0); 18846303Smarkm} 18918316Swollman 19018316Swollman/* 19118316Swollman * Fill the input buffer, do chunk decoding on the fly 19218316Swollman */ 19318316Swollmanstatic int 19446303Smarkmhttp_fillbuf(struct httpio *io, size_t len) 19546303Smarkm{ 19618316Swollman if (io->error) 19718316Swollman return (-1); 19818316Swollman if (io->eof) 19918316Swollman return (0); 20018316Swollman 20118316Swollman if (io->chunked == 0) { 20218316Swollman if (http_growbuf(io, len) == -1) 20346303Smarkm return (-1); 20420339Swollman if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { 20520339Swollman io->error = 1; 20620339Swollman return (-1); 20718316Swollman } 20846303Smarkm io->bufpos = 0; 20946303Smarkm return (io->buflen); 21046303Smarkm } 21146303Smarkm 21246303Smarkm if (io->chunksize == 0) { 21346303Smarkm switch (http_new_chunk(io)) { 21418316Swollman case -1: 21518316Swollman io->error = 1; 21620339Swollman return (-1); 21718316Swollman case 0: 21818316Swollman io->eof = 1; 21918316Swollman return (0); 22018316Swollman } 22118316Swollman } 22218316Swollman 22318316Swollman if (len > io->chunksize) 22418316Swollman len = io->chunksize; 22518316Swollman if (http_growbuf(io, len) == -1) 22646303Smarkm return (-1); 22718316Swollman if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { 22818316Swollman io->error = 1; 22918316Swollman return (-1); 23018316Swollman } 23118316Swollman io->chunksize -= io->buflen; 23246303Smarkm 23318316Swollman if (io->chunksize == 0) { 23418316Swollman char endl[2]; 23518316Swollman 23618316Swollman if (fetch_read(io->conn, endl, 2) != 2 || 23718316Swollman endl[0] != '\r' || endl[1] != '\n') 23818316Swollman return (-1); 23918316Swollman } 24018316Swollman 241126250Sbms io->bufpos = 0; 24246303Smarkm 24318316Swollman return (io->buflen); 24446303Smarkm} 24518316Swollman 24618316Swollman/* 24718316Swollman * Read function 24818316Swollman */ 24918316Swollmanstatic int 25020339Swollmanhttp_readfn(void *v, char *buf, int len) 25120339Swollman{ 25218316Swollman struct httpio *io = (struct httpio *)v; 25346303Smarkm int l, pos; 25420339Swollman 25518316Swollman if (io->error) 256190718Sphk return (-1); 25720339Swollman if (io->eof) 25820339Swollman return (0); 25920339Swollman 26020339Swollman for (pos = 0; len > 0; pos += l, len -= l) { 26120339Swollman /* empty buffer */ 262190718Sphk if (!io->buf || io->bufpos == io->buflen) 26320339Swollman if (http_fillbuf(io, len) < 1) 26420339Swollman break; 26520339Swollman l = io->buflen - io->bufpos; 26620339Swollman if (len < l) 26720339Swollman l = len; 26820339Swollman memcpy(buf + pos, io->buf + io->bufpos, l); 26920339Swollman io->bufpos += l; 27020339Swollman } 27120339Swollman 27220339Swollman if (!pos && io->error) 27320339Swollman return (-1); 27420339Swollman return (pos); 27520339Swollman} 27620339Swollman 27720339Swollman/* 27820339Swollman * Write function 27920339Swollman */ 28020339Swollmanstatic int 28120339Swollmanhttp_writefn(void *v, const char *buf, int len) 28220339Swollman{ 28320339Swollman struct httpio *io = (struct httpio *)v; 28420339Swollman 28520339Swollman return (fetch_write(io->conn, buf, len)); 28620339Swollman} 28720339Swollman 28820339Swollman/* 28920339Swollman * Close function 29020339Swollman */ 29120339Swollmanstatic int 29220339Swollmanhttp_closefn(void *v) 29320339Swollman{ 29420339Swollman struct httpio *io = (struct httpio *)v; 29520339Swollman int r; 29620339Swollman 29746303Smarkm r = fetch_close(io->conn); 29846303Smarkm if (io->buf) 29920339Swollman free(io->buf); 30020339Swollman free(io); 30118316Swollman return (r); 30218316Swollman} 30346303Smarkm 30418316Swollman/* 30518316Swollman * Wrap a file descriptor up 30620339Swollman */ 30720339Swollmanstatic FILE * 30820339Swollmanhttp_funopen(conn_t *conn, int chunked) 30918316Swollman{ 31020339Swollman struct httpio *io; 31120339Swollman FILE *f; 31220339Swollman 31320339Swollman if ((io = calloc(1, sizeof(*io))) == NULL) { 31420339Swollman fetch_syserr(); 31520339Swollman return (NULL); 31620339Swollman } 31720339Swollman io->conn = conn; 31820339Swollman io->chunked = chunked; 31920339Swollman f = funopen(io, http_readfn, http_writefn, NULL, http_closefn); 32018316Swollman if (f == NULL) { 32118316Swollman fetch_syserr(); 32218316Swollman free(io); 32318316Swollman return (NULL); 32418316Swollman } 32518316Swollman return (f); 32618316Swollman} 32720339Swollman 32820339Swollman 32920339Swollman/***************************************************************************** 33020339Swollman * Helper functions for talking to the server and parsing its replies 33120339Swollman */ 33220339Swollman 33320339Swollman/* Header types */ 33420339Swollmantypedef enum { 33520339Swollman hdr_syserror = -2, 33620339Swollman hdr_error = -1, 33720339Swollman hdr_end = 0, 33820339Swollman hdr_unknown = 1, 33920339Swollman hdr_content_length, 34020339Swollman hdr_content_range, 34120339Swollman hdr_last_modified, 34220339Swollman hdr_location, 34320339Swollman hdr_transfer_encoding, 34420339Swollman hdr_www_authenticate 34546303Smarkm} hdr_t; 34618316Swollman 34718316Swollman/* Names of interesting headers */ 34818316Swollmanstatic struct { 34918316Swollman hdr_t num; 35018316Swollman const char *name; 35120339Swollman} hdr_names[] = { 35220339Swollman { hdr_content_length, "Content-Length" }, 35320339Swollman { hdr_content_range, "Content-Range" }, 35420339Swollman { hdr_last_modified, "Last-Modified" }, 35520339Swollman { hdr_location, "Location" }, 35620339Swollman { hdr_transfer_encoding, "Transfer-Encoding" }, 35720339Swollman { hdr_www_authenticate, "WWW-Authenticate" }, 35820339Swollman { hdr_unknown, NULL }, 35920339Swollman}; 36020339Swollman 36118316Swollman/* 36218316Swollman * Send a formatted line; optionally echo to terminal 36318316Swollman */ 36446303Smarkmstatic int 36518316Swollmanhttp_cmd(conn_t *conn, const char *fmt, ...) 36646303Smarkm{ 36718316Swollman va_list ap; 36820339Swollman size_t len; 36920339Swollman char *msg; 37020339Swollman int r; 37118316Swollman 37220339Swollman va_start(ap, fmt); 37320339Swollman len = vasprintf(&msg, fmt, ap); 37420339Swollman va_end(ap); 37520339Swollman 37620339Swollman if (msg == NULL) { 37719880Swollman errno = ENOMEM; 37820339Swollman fetch_syserr(); 37920339Swollman return (-1); 38020339Swollman } 38118316Swollman 38218316Swollman r = fetch_putln(conn, msg, len); 38318316Swollman free(msg); 38418316Swollman 38518316Swollman if (r == -1) { 38646303Smarkm fetch_syserr(); 38718316Swollman return (-1); 38818316Swollman } 38946303Smarkm 39018316Swollman return (0); 39118316Swollman} 39218316Swollman 39318316Swollman/* 39418316Swollman * Get and parse status line 39546303Smarkm */ 39618316Swollmanstatic int 39718316Swollmanhttp_get_reply(conn_t *conn) 39846303Smarkm{ 39918316Swollman char *p; 40018316Swollman 40118316Swollman if (fetch_getln(conn) == -1) 40220339Swollman return (-1); 40318316Swollman /* 40418316Swollman * A valid status line looks like "HTTP/m.n xyz reason" where m 40520339Swollman * and n are the major and minor protocol version numbers and xyz 40618316Swollman * is the reply code. 40720339Swollman * Unfortunately, there are servers out there (NCSA 1.5.1, to name 40820339Swollman * just one) that do not send a version number, so we can't rely 40918316Swollman * on finding one, but if we do, insist on it being 1.0 or 1.1. 41020339Swollman * We don't care about the reason phrase. 41120339Swollman */ 41220339Swollman if (strncmp(conn->buf, "HTTP", 4) != 0) 41320339Swollman return (HTTP_PROTOCOL_ERROR); 41420339Swollman p = conn->buf + 4; 41520339Swollman if (*p == '/') { 41620339Swollman if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1')) 41720339Swollman return (HTTP_PROTOCOL_ERROR); 41820339Swollman p += 4; 41920339Swollman } 42018316Swollman if (*p != ' ' || 42118316Swollman !isdigit((unsigned char)p[1]) || 42220339Swollman !isdigit((unsigned char)p[2]) || 42320339Swollman !isdigit((unsigned char)p[3])) 42418316Swollman return (HTTP_PROTOCOL_ERROR); 42518316Swollman 42618316Swollman conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0'); 42718316Swollman return (conn->err); 42818316Swollman} 42918316Swollman 43018316Swollman/* 43118316Swollman * Check a header; if the type matches the given string, return a pointer 43218316Swollman * to the beginning of the value. 43318316Swollman */ 43418316Swollmanstatic const char * 43518316Swollmanhttp_match(const char *str, const char *hdr) 43618316Swollman{ 43718316Swollman while (*str && *hdr && 43818316Swollman tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++)) 43918316Swollman /* nothing */; 44018316Swollman if (*str || *hdr != ':') 44118316Swollman return (NULL); 44218316Swollman while (*hdr && isspace((unsigned char)*++hdr)) 44318316Swollman /* nothing */; 44418316Swollman return (hdr); 44518316Swollman} 44618316Swollman 44718316Swollman/* 44818316Swollman * Get the next header and return the appropriate symbolic code. 44918316Swollman */ 45018316Swollmanstatic hdr_t 45118316Swollmanhttp_next_header(conn_t *conn, const char **p) 45218316Swollman{ 45318316Swollman int i; 45418316Swollman 45518316Swollman if (fetch_getln(conn) == -1) 45618316Swollman return (hdr_syserror); 45718316Swollman while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1])) 45818316Swollman conn->buflen--; 45918316Swollman conn->buf[conn->buflen] = '\0'; 46018316Swollman if (conn->buflen == 0) 46118316Swollman return (hdr_end); 46218316Swollman /* 46318316Swollman * We could check for malformed headers but we don't really care. 46418316Swollman * A valid header starts with a token immediately followed by a 46518316Swollman * colon; a token is any sequence of non-control, non-whitespace 46618316Swollman * characters except "()<>@,;:\\\"{}". 46718316Swollman */ 46818316Swollman for (i = 0; hdr_names[i].num != hdr_unknown; i++) 46946303Smarkm if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL) 47046303Smarkm return (hdr_names[i].num); 47146303Smarkm return (hdr_unknown); 47218316Swollman} 47318316Swollman 474190718Sphk/* 47518316Swollman * Parse a last-modified header 47618316Swollman */ 47718316Swollmanstatic int 47818316Swollmanhttp_parse_mtime(const char *p, time_t *mtime) 47918316Swollman{ 480190718Sphk char locale[64], *r; 48119880Swollman struct tm tm; 48218316Swollman 48319880Swollman strncpy(locale, setlocale(LC_TIME, NULL), sizeof(locale)); 48419880Swollman setlocale(LC_TIME, "C"); 48518316Swollman r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 48618316Swollman /* XXX should add support for date-2 and date-3 */ 48718316Swollman setlocale(LC_TIME, locale); 48818316Swollman if (r == NULL) 48918316Swollman return (-1); 49018316Swollman DEBUG(fprintf(stderr, "last modified: [%04d-%02d-%02d " 49118316Swollman "%02d:%02d:%02d]\n", 49218316Swollman tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 49318316Swollman tm.tm_hour, tm.tm_min, tm.tm_sec)); 49419880Swollman *mtime = timegm(&tm); 49518316Swollman return (0); 49618316Swollman} 49719880Swollman 49820339Swollman/* 49918316Swollman * Parse a content-length header 50018316Swollman */ 50118316Swollmanstatic int 50218316Swollmanhttp_parse_length(const char *p, off_t *length) 50318316Swollman{ 50418316Swollman off_t len; 50518316Swollman 50618316Swollman for (len = 0; *p && isdigit((unsigned char)*p); ++p) 50718316Swollman len = len * 10 + (*p - '0'); 50818316Swollman if (*p) 50918316Swollman return (-1); 51018316Swollman DEBUG(fprintf(stderr, "content length: [%lld]\n", 51118316Swollman (long long)len)); 51218316Swollman *length = len; 51318316Swollman return (0); 51418316Swollman} 51518316Swollman 51618316Swollman/* 51718316Swollman * Parse a content-range header 51818316Swollman */ 51918316Swollmanstatic int 52019880Swollmanhttp_parse_range(const char *p, off_t *offset, off_t *length, off_t *size) 52118316Swollman{ 52218316Swollman off_t first, last, len; 52318316Swollman 524190718Sphk if (strncasecmp(p, "bytes ", 6) != 0) 52518316Swollman return (-1); 52618316Swollman p += 6; 52718316Swollman if (*p == '*') { 52818316Swollman first = last = -1; 52918316Swollman ++p; 53018316Swollman } else { 53118316Swollman for (first = 0; *p && isdigit((unsigned char)*p); ++p) 53218316Swollman first = first * 10 + *p - '0'; 53318316Swollman if (*p != '-') 53418316Swollman return (-1); 53518316Swollman for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p) 53618316Swollman last = last * 10 + *p - '0'; 53718316Swollman } 53846303Smarkm if (first > last || *p != '/') 53918316Swollman return (-1); 54018316Swollman for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p) 54118316Swollman len = len * 10 + *p - '0'; 54246303Smarkm if (*p || len < last - first + 1) 54318316Swollman return (-1); 54418316Swollman if (first == -1) { 54518316Swollman DEBUG(fprintf(stderr, "content range: [*/%lld]\n", 54618316Swollman (long long)len)); 54718316Swollman *length = 0; 54818316Swollman } else { 54918316Swollman DEBUG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n", 55018316Swollman (long long)first, (long long)last, (long long)len)); 55118316Swollman *length = last - first + 1; 55218316Swollman } 55318316Swollman *offset = first; 55418316Swollman *size = len; 55518316Swollman return (0); 55618316Swollman} 55718316Swollman 55818316Swollman 55918316Swollman/***************************************************************************** 56018316Swollman * Helper functions for authorization 56118316Swollman */ 56218316Swollman 56318316Swollman/* 56418316Swollman * Base64 encoding 56518316Swollman */ 56618316Swollmanstatic char * 56718316Swollmanhttp_base64(const char *src) 56818316Swollman{ 56918316Swollman static const char base64[] = 57018316Swollman "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 57118316Swollman "abcdefghijklmnopqrstuvwxyz" 57218316Swollman "0123456789+/"; 57318316Swollman char *str, *dst; 57418316Swollman size_t l; 57518316Swollman int t, r; 57618316Swollman 57718316Swollman l = strlen(src); 57846303Smarkm if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL) 57946303Smarkm return (NULL); 58046303Smarkm dst = str; 58146303Smarkm r = 0; 58218316Swollman 58318316Swollman while (l >= 3) { 58418316Swollman t = (src[0] << 16) | (src[1] << 8) | src[2]; 58518316Swollman dst[0] = base64[(t >> 18) & 0x3f]; 58618316Swollman dst[1] = base64[(t >> 12) & 0x3f]; 58718316Swollman dst[2] = base64[(t >> 6) & 0x3f]; 58846303Smarkm dst[3] = base64[(t >> 0) & 0x3f]; 58918316Swollman src += 3; l -= 3; 59018316Swollman dst += 4; r += 4; 59118316Swollman } 59218316Swollman 59320339Swollman switch (l) { 59420339Swollman case 2: 59520339Swollman t = (src[0] << 16) | (src[1] << 8); 59620339Swollman dst[0] = base64[(t >> 18) & 0x3f]; 59720339Swollman dst[1] = base64[(t >> 12) & 0x3f]; 59820339Swollman dst[2] = base64[(t >> 6) & 0x3f]; 59920339Swollman dst[3] = '='; 60020339Swollman dst += 4; 60146303Smarkm r += 4; 60246303Smarkm break; 60346303Smarkm case 1: 60420339Swollman t = src[0] << 16; 60520339Swollman dst[0] = base64[(t >> 18) & 0x3f]; 60620339Swollman dst[1] = base64[(t >> 12) & 0x3f]; 60720339Swollman dst[2] = dst[3] = '='; 60820339Swollman dst += 4; 60920339Swollman r += 4; 61020339Swollman break; 61120339Swollman case 0: 61220339Swollman break; 61320339Swollman } 61420339Swollman 61520339Swollman *dst = 0; 61620339Swollman return (str); 61746303Smarkm} 61846303Smarkm 61946303Smarkm/* 62046303Smarkm * Encode username and password 62146303Smarkm */ 62246303Smarkmstatic int 62346303Smarkmhttp_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd) 62420339Swollman{ 62520339Swollman char *upw, *auth; 62620339Swollman int r; 62718316Swollman 62846303Smarkm DEBUG(fprintf(stderr, "usr: [%s]\n", usr)); 62946303Smarkm DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd)); 63018316Swollman if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 63118316Swollman return (-1); 63218316Swollman auth = http_base64(upw); 63318316Swollman free(upw); 63418316Swollman if (auth == NULL) 63519880Swollman return (-1); 63618316Swollman r = http_cmd(conn, "%s: Basic %s", hdr, auth); 63718316Swollman free(auth); 63819880Swollman return (r); 63919880Swollman} 64019880Swollman 64118316Swollman/* 64218316Swollman * Send an authorization header 64318316Swollman */ 644126250Sbmsstatic int 645126250Sbmshttp_authorize(conn_t *conn, const char *hdr, const char *p) 646126250Sbms{ 647126250Sbms /* basic authorization */ 648126250Sbms if (strncasecmp(p, "basic:", 6) == 0) { 649126250Sbms char *user, *pwd, *str; 65019880Swollman int r; 65119880Swollman 652126250Sbms /* skip realm */ 65318316Swollman for (p += 6; *p && *p != ':'; ++p) 65418316Swollman /* nothing */ ; 65518316Swollman if (!*p || strchr(++p, ':') == NULL) 65618316Swollman return (-1); 65718316Swollman if ((str = strdup(p)) == NULL) 65818316Swollman return (-1); /* XXX */ 65918316Swollman user = str; 66018316Swollman pwd = strchr(str, ':'); 66118316Swollman *pwd++ = '\0'; 66246303Smarkm r = http_basic_auth(conn, hdr, user, pwd); 66318316Swollman free(str); 66418316Swollman return (r); 66518316Swollman } 66620339Swollman return (-1); 66746303Smarkm} 66846303Smarkm 66946303Smarkm 67046303Smarkm/***************************************************************************** 67146303Smarkm * Helper functions for connecting to a server or proxy 67218316Swollman */ 67318316Swollman 67418316Swollman/* 67546303Smarkm * Connect to the correct HTTP server or proxy. 67646303Smarkm */ 67746303Smarkmstatic conn_t * 67846303Smarkmhttp_connect(struct url *URL, struct url *purl, const char *flags) 67946303Smarkm{ 68046303Smarkm conn_t *conn; 68146303Smarkm int verbose; 68246303Smarkm int af, val; 68346303Smarkm 68418316Swollman#ifdef INET6 68546303Smarkm af = AF_UNSPEC; 68646303Smarkm#else 68720339Swollman af = AF_INET; 68846303Smarkm#endif 68946303Smarkm 69020339Swollman verbose = CHECK_FLAG('v'); 69120339Swollman if (CHECK_FLAG('4')) 69218316Swollman af = AF_INET; 69320339Swollman#ifdef INET6 69446303Smarkm else if (CHECK_FLAG('6')) 69546303Smarkm af = AF_INET6; 69646303Smarkm#endif 69746303Smarkm 69846303Smarkm if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { 69920339Swollman URL = purl; 70046303Smarkm } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) { 70146303Smarkm /* can't talk http to an ftp server */ 70246303Smarkm /* XXX should set an error code */ 70346303Smarkm return (NULL); 70418316Swollman } 70518316Swollman 70618316Swollman if ((conn = fetch_connect(URL->host, URL->port, af, verbose)) == NULL) 70746303Smarkm /* fetch_connect() has already set an error code */ 70846303Smarkm return (NULL); 70946303Smarkm if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && 71046303Smarkm fetch_ssl(conn, verbose) == -1) { 71146303Smarkm fetch_close(conn); 71218316Swollman /* grrr */ 71320339Swollman errno = EAUTH; 71418316Swollman fetch_syserr(); 71518316Swollman return (NULL); 71618316Swollman } 71746303Smarkm 71818316Swollman val = 1; 71918316Swollman setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); 72046303Smarkm 72118316Swollman return (conn); 72218316Swollman} 72318316Swollman 72446303Smarkmstatic struct url * 72518316Swollmanhttp_get_proxy(struct url * url, const char *flags) 72618316Swollman{ 72718316Swollman struct url *purl; 72818316Swollman char *p; 72918316Swollman 730126250Sbms if (flags != NULL && strchr(flags, 'd') != NULL) 73146303Smarkm return (NULL); 73218316Swollman if (fetch_no_proxy_match(url->host)) 73318316Swollman return (NULL); 73418316Swollman if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 73518316Swollman *p && (purl = fetchParseURL(p))) { 73618316Swollman if (!*purl->scheme) 73718316Swollman strcpy(purl->scheme, SCHEME_HTTP); 73846303Smarkm if (!purl->port) 73918316Swollman purl->port = fetch_default_proxy_port(purl->scheme); 74018316Swollman if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 74118316Swollman return (purl); 74218316Swollman fetchFreeURL(purl); 74318316Swollman } 74418316Swollman return (NULL); 74518316Swollman} 74618316Swollman 74718316Swollmanstatic void 748126250Sbmshttp_print_html(FILE *out, FILE *in) 74919880Swollman{ 75018316Swollman size_t len; 75118316Swollman char *line, *p, *q; 75218316Swollman int comment, tag; 75318316Swollman 75418316Swollman comment = tag = 0; 75518316Swollman while ((line = fgetln(in, &len)) != NULL) { 75646303Smarkm while (len && isspace((unsigned char)line[len - 1])) 75718316Swollman --len; 75818316Swollman for (p = q = line; q < line + len; ++q) { 75918316Swollman if (comment && *q == '-') { 76018316Swollman if (q + 2 < line + len && 76118316Swollman strcmp(q, "-->") == 0) { 76218316Swollman tag = comment = 0; 76318316Swollman q += 2; 76418316Swollman } 76518316Swollman } else if (tag && !comment && *q == '>') { 766126250Sbms p = q + 1; 76719880Swollman tag = 0; 76818316Swollman } else if (!tag && *q == '<') { 76918316Swollman if (q > p) 77018316Swollman fwrite(p, q - p, 1, out); 77118316Swollman tag = 1; 77218316Swollman if (q + 3 < line + len && 77318316Swollman strcmp(q, "<!--") == 0) { 77446303Smarkm comment = 1; 77546303Smarkm q += 3; 77618316Swollman } 77718316Swollman } 77818316Swollman } 77918316Swollman if (!tag && q > p) 78046303Smarkm fwrite(p, q - p, 1, out); 78146303Smarkm fputc('\n', out); 78246303Smarkm } 78318316Swollman} 78446303Smarkm 78546303Smarkm 78618316Swollman/***************************************************************************** 78718316Swollman * Core 78818316Swollman */ 78920339Swollman 79018316Swollman/* 79146303Smarkm * Send a request and process the reply 79220339Swollman * 79320339Swollman * XXX This function is way too long, the do..while loop should be split 79418316Swollman * XXX off into a separate function. 79518316Swollman */ 79620339SwollmanFILE * 79746303Smarkmhttp_request(struct url *URL, const char *op, struct url_stat *us, 79846303Smarkm struct url *purl, const char *flags) 79946303Smarkm{ 80046303Smarkm conn_t *conn; 80146303Smarkm struct url *url, *new; 80246303Smarkm int chunked, direct, need_auth, noredirect, verbose; 80320339Swollman int e, i, n, val; 80446303Smarkm off_t offset, clength, length, size; 80546303Smarkm time_t mtime; 80646303Smarkm const char *p; 80718316Swollman FILE *f; 80818316Swollman hdr_t h; 80920339Swollman char hbuf[MAXHOSTNAMELEN + 7], *host; 81018316Swollman 81118316Swollman direct = CHECK_FLAG('d'); 81218316Swollman noredirect = CHECK_FLAG('A'); 81318316Swollman verbose = CHECK_FLAG('v'); 81446303Smarkm 81518316Swollman if (direct && purl) { 81618316Swollman fetchFreeURL(purl); 81718316Swollman purl = NULL; 81818316Swollman } 81918316Swollman 82020339Swollman /* try the provided URL first */ 82118316Swollman url = URL; 82246303Smarkm 82320339Swollman /* if the A flag is set, we only get one try */ 82420339Swollman n = noredirect ? 1 : MAX_REDIRECT; 82520339Swollman i = 0; 82618316Swollman 82718316Swollman e = HTTP_PROTOCOL_ERROR; 82818316Swollman need_auth = 0; 82918316Swollman do { 83018316Swollman new = NULL; 83118316Swollman chunked = 0; 83246303Smarkm offset = 0; 83318316Swollman clength = -1; 83418316Swollman length = -1; 83518316Swollman size = -1; 83646303Smarkm mtime = 0; 83718316Swollman 83846303Smarkm /* check port */ 83946303Smarkm if (!url->port) 84046303Smarkm url->port = fetch_default_port(url->scheme); 84118316Swollman 84220339Swollman /* were we redirected to an FTP URL? */ 84320339Swollman if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) { 84420339Swollman if (strcmp(op, "GET") == 0) 84546303Smarkm return (ftp_request(url, "RETR", us, purl, flags)); 84618316Swollman else if (strcmp(op, "HEAD") == 0) 84718316Swollman return (ftp_request(url, "STAT", us, purl, flags)); 84818316Swollman } 84920339Swollman 85020339Swollman /* connect to server or proxy */ 85120339Swollman if ((conn = http_connect(url, purl, flags)) == NULL) 85220339Swollman goto ouch; 85318316Swollman 85418316Swollman host = url->host; 85518316Swollman#ifdef INET6 85618316Swollman if (strchr(url->host, ':')) { 85718316Swollman snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); 85818316Swollman host = hbuf; 85918316Swollman } 86018316Swollman#endif 86118316Swollman if (url->port != fetch_default_port(url->scheme)) { 86218316Swollman if (host != hbuf) { 86318316Swollman strcpy(hbuf, host); 86419880Swollman host = hbuf; 86519880Swollman } 86618316Swollman snprintf(hbuf + strlen(hbuf), 86718316Swollman sizeof(hbuf) - strlen(hbuf), ":%d", url->port); 86818316Swollman } 86918316Swollman 87020339Swollman /* send request */ 871190711Sphk if (verbose) 87219880Swollman fetch_info("requesting %s://%s%s", 87318316Swollman url->scheme, host, url->doc); 87418316Swollman if (purl) { 87518316Swollman http_cmd(conn, "%s %s://%s%s HTTP/1.1", 87618316Swollman op, url->scheme, host, url->doc); 87718316Swollman } else { 87846303Smarkm http_cmd(conn, "%s %s HTTP/1.1", 87918316Swollman op, url->doc); 88018316Swollman } 88118316Swollman 88218316Swollman /* virtual host */ 88318316Swollman http_cmd(conn, "Host: %s", host); 88418316Swollman 88546303Smarkm /* proxy authorization */ 88619880Swollman if (purl) { 88718316Swollman if (*purl->user || *purl->pwd) 88818316Swollman http_basic_auth(conn, "Proxy-Authorization", 88918316Swollman purl->user, purl->pwd); 89018316Swollman else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') 89118316Swollman http_authorize(conn, "Proxy-Authorization", p); 89218316Swollman } 89318316Swollman 89418316Swollman /* server authorization */ 89518316Swollman if (need_auth || *url->user || *url->pwd) { 89618316Swollman if (*url->user || *url->pwd) 89718316Swollman http_basic_auth(conn, "Authorization", url->user, url->pwd); 89818316Swollman else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') 89918316Swollman http_authorize(conn, "Authorization", p); 90018316Swollman else if (fetchAuthMethod && fetchAuthMethod(url) == 0) { 90118316Swollman http_basic_auth(conn, "Authorization", url->user, url->pwd); 90218316Swollman } else { 90318316Swollman http_seterr(HTTP_NEED_AUTH); 90418316Swollman goto ouch; 90518316Swollman } 90618316Swollman } 90718316Swollman 90818316Swollman /* other headers */ 90918316Swollman if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') { 91019880Swollman if (strcasecmp(p, "auto") == 0) 91118316Swollman http_cmd(conn, "Referer: %s://%s%s", 91218316Swollman url->scheme, host, url->doc); 91318316Swollman else 91418316Swollman http_cmd(conn, "Referer: %s", p); 91518316Swollman } 91618316Swollman if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') 91719880Swollman http_cmd(conn, "User-Agent: %s", p); 91819880Swollman else 91918316Swollman http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname()); 92019880Swollman if (url->offset > 0) 92119880Swollman http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset); 92219880Swollman http_cmd(conn, "Connection: close"); 92319880Swollman http_cmd(conn, ""); 92418316Swollman 92518316Swollman /* 92618316Swollman * Force the queued request to be dispatched. Normally, one 92718316Swollman * would do this with shutdown(2) but squid proxies can be 92818316Swollman * configured to disallow such half-closed connections. To 92918316Swollman * be compatible with such configurations, fiddle with socket 93018316Swollman * options to force the pending data to be written. 93118316Swollman */ 93219880Swollman val = 0; 93318316Swollman setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, 93418316Swollman sizeof(val)); 93519880Swollman val = 1; 93618316Swollman setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, 93718316Swollman sizeof(val)); 93818316Swollman 93918316Swollman /* get reply */ 94018316Swollman switch (http_get_reply(conn)) { 94118316Swollman case HTTP_OK: 94219880Swollman case HTTP_PARTIAL: 94319880Swollman /* fine */ 94419880Swollman break; 94519880Swollman case HTTP_MOVED_PERM: 94619880Swollman case HTTP_MOVED_TEMP: 94719880Swollman case HTTP_SEE_OTHER: 94819880Swollman /* 94919880Swollman * Not so fine, but we still have to read the 95019880Swollman * headers to get the new location. 95119880Swollman */ 95219880Swollman break; 95319880Swollman case HTTP_NEED_AUTH: 95419880Swollman if (need_auth) { 95546303Smarkm /* 95646303Smarkm * We already sent out authorization code, 95746303Smarkm * so there's nothing more we can do. 95846303Smarkm */ 95919880Swollman http_seterr(conn->err); 96046303Smarkm goto ouch; 96146303Smarkm } 96246303Smarkm /* try again, but send the password this time */ 96346303Smarkm if (verbose) 96446303Smarkm fetch_info("server requires authorization"); 96546303Smarkm break; 96619880Swollman case HTTP_NEED_PROXY_AUTH: 96719880Swollman /* 96818316Swollman * If we're talking to a proxy, we already sent 96946303Smarkm * our proxy authorization code, so there's 97019880Swollman * nothing more we can do. 97118316Swollman */ 97246303Smarkm http_seterr(conn->err); 97318316Swollman goto ouch; 97418316Swollman case HTTP_BAD_RANGE: 97519880Swollman /* 97618316Swollman * This can happen if we ask for 0 bytes because 97718316Swollman * we already have the whole file. Consider this 97818316Swollman * a success for now, and check sizes later. 97918316Swollman */ 98019880Swollman break; 98118316Swollman case HTTP_PROTOCOL_ERROR: 98218316Swollman /* fall through */ 98319880Swollman case -1: 98418316Swollman fetch_syserr(); 98518316Swollman goto ouch; 98618316Swollman default: 98718316Swollman http_seterr(conn->err); 98818316Swollman if (!verbose) 98918316Swollman goto ouch; 99018316Swollman /* fall through so we can get the full error message */ 99118316Swollman } 99218316Swollman 99318316Swollman /* get headers */ 99418316Swollman do { 99518316Swollman switch ((h = http_next_header(conn, &p))) { 99618316Swollman case hdr_syserror: 99718316Swollman fetch_syserr(); 99818316Swollman goto ouch; 99918316Swollman case hdr_error: 100018316Swollman http_seterr(HTTP_PROTOCOL_ERROR); 100118316Swollman goto ouch; 100218316Swollman case hdr_content_length: 100318316Swollman http_parse_length(p, &clength); 100418316Swollman break; 100518316Swollman case hdr_content_range: 100618316Swollman http_parse_range(p, &offset, &length, &size); 100718316Swollman break; 100818316Swollman case hdr_last_modified: 100918316Swollman http_parse_mtime(p, &mtime); 101018316Swollman break; 101118316Swollman case hdr_location: 101218316Swollman if (!HTTP_REDIRECT(conn->err)) 101318316Swollman break; 101420339Swollman if (new) 101520339Swollman free(new); 101618316Swollman if (verbose) 101718316Swollman fetch_info("%d redirect to %s", conn->err, p); 101818316Swollman if (*p == '/') 101918316Swollman /* absolute path */ 102018316Swollman new = fetchMakeURL(url->scheme, url->host, url->port, p, 102118316Swollman url->user, url->pwd); 1022 else 1023 new = fetchParseURL(p); 1024 if (new == NULL) { 1025 /* XXX should set an error code */ 1026 DEBUG(fprintf(stderr, "failed to parse new URL\n")); 1027 goto ouch; 1028 } 1029 if (!*new->user && !*new->pwd) { 1030 strcpy(new->user, url->user); 1031 strcpy(new->pwd, url->pwd); 1032 } 1033 new->offset = url->offset; 1034 new->length = url->length; 1035 break; 1036 case hdr_transfer_encoding: 1037 /* XXX weak test*/ 1038 chunked = (strcasecmp(p, "chunked") == 0); 1039 break; 1040 case hdr_www_authenticate: 1041 if (conn->err != HTTP_NEED_AUTH) 1042 break; 1043 /* if we were smarter, we'd check the method and realm */ 1044 break; 1045 case hdr_end: 1046 /* fall through */ 1047 case hdr_unknown: 1048 /* ignore */ 1049 break; 1050 } 1051 } while (h > hdr_end); 1052 1053 /* we need to provide authentication */ 1054 if (conn->err == HTTP_NEED_AUTH) { 1055 e = conn->err; 1056 need_auth = 1; 1057 fetch_close(conn); 1058 conn = NULL; 1059 continue; 1060 } 1061 1062 /* requested range not satisfiable */ 1063 if (conn->err == HTTP_BAD_RANGE) { 1064 if (url->offset == size && url->length == 0) { 1065 /* asked for 0 bytes; fake it */ 1066 offset = url->offset; 1067 clength = -1; 1068 conn->err = HTTP_OK; 1069 break; 1070 } else { 1071 http_seterr(conn->err); 1072 goto ouch; 1073 } 1074 } 1075 1076 /* we have a hit or an error */ 1077 if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err)) 1078 break; 1079 1080 /* all other cases: we got a redirect */ 1081 e = conn->err; 1082 need_auth = 0; 1083 fetch_close(conn); 1084 conn = NULL; 1085 if (!new) { 1086 DEBUG(fprintf(stderr, "redirect with no new location\n")); 1087 break; 1088 } 1089 if (url != URL) 1090 fetchFreeURL(url); 1091 url = new; 1092 } while (++i < n); 1093 1094 /* we failed, or ran out of retries */ 1095 if (conn == NULL) { 1096 http_seterr(e); 1097 goto ouch; 1098 } 1099 1100 DEBUG(fprintf(stderr, "offset %lld, length %lld," 1101 " size %lld, clength %lld\n", 1102 (long long)offset, (long long)length, 1103 (long long)size, (long long)clength)); 1104 1105 /* check for inconsistencies */ 1106 if (clength != -1 && length != -1 && clength != length) { 1107 http_seterr(HTTP_PROTOCOL_ERROR); 1108 goto ouch; 1109 } 1110 if (clength == -1) 1111 clength = length; 1112 if (clength != -1) 1113 length = offset + clength; 1114 if (length != -1 && size != -1 && length != size) { 1115 http_seterr(HTTP_PROTOCOL_ERROR); 1116 goto ouch; 1117 } 1118 if (size == -1) 1119 size = length; 1120 1121 /* fill in stats */ 1122 if (us) { 1123 us->size = size; 1124 us->atime = us->mtime = mtime; 1125 } 1126 1127 /* too far? */ 1128 if (URL->offset > 0 && offset > URL->offset) { 1129 http_seterr(HTTP_PROTOCOL_ERROR); 1130 goto ouch; 1131 } 1132 1133 /* report back real offset and size */ 1134 URL->offset = offset; 1135 URL->length = clength; 1136 1137 /* wrap it up in a FILE */ 1138 if ((f = http_funopen(conn, chunked)) == NULL) { 1139 fetch_syserr(); 1140 goto ouch; 1141 } 1142 1143 if (url != URL) 1144 fetchFreeURL(url); 1145 if (purl) 1146 fetchFreeURL(purl); 1147 1148 if (HTTP_ERROR(conn->err)) { 1149 http_print_html(stderr, f); 1150 fclose(f); 1151 f = NULL; 1152 } 1153 1154 return (f); 1155 1156ouch: 1157 if (url != URL) 1158 fetchFreeURL(url); 1159 if (purl) 1160 fetchFreeURL(purl); 1161 if (conn != NULL) 1162 fetch_close(conn); 1163 return (NULL); 1164} 1165 1166 1167/***************************************************************************** 1168 * Entry points 1169 */ 1170 1171/* 1172 * Retrieve and stat a file by HTTP 1173 */ 1174FILE * 1175fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags) 1176{ 1177 return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags)); 1178} 1179 1180/* 1181 * Retrieve a file by HTTP 1182 */ 1183FILE * 1184fetchGetHTTP(struct url *URL, const char *flags) 1185{ 1186 return (fetchXGetHTTP(URL, NULL, flags)); 1187} 1188 1189/* 1190 * Store a file by HTTP 1191 */ 1192FILE * 1193fetchPutHTTP(struct url *URL __unused, const char *flags __unused) 1194{ 1195 warnx("fetchPutHTTP(): not implemented"); 1196 return (NULL); 1197} 1198 1199/* 1200 * Get an HTTP document's metadata 1201 */ 1202int 1203fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags) 1204{ 1205 FILE *f; 1206 1207 f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags); 1208 if (f == NULL) 1209 return (-1); 1210 fclose(f); 1211 return (0); 1212} 1213 1214/* 1215 * List a directory 1216 */ 1217struct url_ent * 1218fetchListHTTP(struct url *url __unused, const char *flags __unused) 1219{ 1220 warnx("fetchListHTTP(): not implemented"); 1221 return (NULL); 1222} 1223