http.c revision 62965
137535Sdes/*- 237535Sdes * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 337535Sdes * All rights reserved. 437535Sdes * 537535Sdes * Redistribution and use in source and binary forms, with or without 637535Sdes * modification, are permitted provided that the following conditions 737535Sdes * are met: 837535Sdes * 1. Redistributions of source code must retain the above copyright 937535Sdes * notice, this list of conditions and the following disclaimer 1037535Sdes * in this position and unchanged. 1137535Sdes * 2. Redistributions in binary form must reproduce the above copyright 1237535Sdes * notice, this list of conditions and the following disclaimer in the 1337535Sdes * documentation and/or other materials provided with the distribution. 1437535Sdes * 3. The name of the author may not be used to endorse or promote products 1537535Sdes * derived from this software without specific prior written permission 1637535Sdes * 1737535Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1837535Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1937535Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2037535Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2137535Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2237535Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2337535Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2437535Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2537535Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2637535Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2737535Sdes * 2850476Speter * $FreeBSD: head/lib/libfetch/http.c 62965 2000-07-11 18:12:41Z des $ 2937535Sdes */ 3037535Sdes 3137608Sdes/* 3237608Sdes * The base64 code in this file is based on code from MIT fetch, which 3337608Sdes * has the following copyright and license: 3437608Sdes * 3537608Sdes *- 3637608Sdes * Copyright 1997 Massachusetts Institute of Technology 3737608Sdes * 3837608Sdes * Permission to use, copy, modify, and distribute this software and 3937608Sdes * its documentation for any purpose and without fee is hereby 4037608Sdes * granted, provided that both the above copyright notice and this 4137608Sdes * permission notice appear in all copies, that both the above 4237608Sdes * copyright notice and this permission notice appear in all 4337608Sdes * supporting documentation, and that the name of M.I.T. not be used 4437608Sdes * in advertising or publicity pertaining to distribution of the 4560189Sdes * software without specific, written prior permission. M.I.T. makes 4637608Sdes * no representations about the suitability of this software for any 4737608Sdes * purpose. It is provided "as is" without express or implied 4837608Sdes * warranty. 4937608Sdes * 5037608Sdes * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 5137608Sdes * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 5237608Sdes * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 5337608Sdes * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 5437608Sdes * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 5537608Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 5637608Sdes * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 5737608Sdes * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 5837608Sdes * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 5937608Sdes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 6037608Sdes * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6137608Sdes * SUCH DAMAGE. */ 6237608Sdes 6337535Sdes#include <sys/param.h> 6460737Sume#include <sys/socket.h> 6537535Sdes 6637535Sdes#include <err.h> 6737535Sdes#include <ctype.h> 6860376Sdes#include <locale.h> 6960189Sdes#include <netdb.h> 7037608Sdes#include <stdarg.h> 7137535Sdes#include <stdio.h> 7237535Sdes#include <stdlib.h> 7337535Sdes#include <string.h> 7460376Sdes#include <time.h> 7537535Sdes#include <unistd.h> 7637535Sdes 7737535Sdes#include "fetch.h" 7840939Sdes#include "common.h" 7941862Sdes#include "httperr.h" 8037535Sdes 8137535Sdesextern char *__progname; 8237535Sdes 8337535Sdes#define ENDL "\r\n" 8437535Sdes 8560196Sdes#define HTTP_OK 200 8660196Sdes#define HTTP_PARTIAL 206 8760954Sdes#define HTTP_MOVED 302 8860196Sdes 8937535Sdesstruct cookie 9037535Sdes{ 9137535Sdes FILE *real_f; 9237535Sdes#define ENC_NONE 0 9337535Sdes#define ENC_CHUNKED 1 9437535Sdes int encoding; /* 1 = chunked, 0 = none */ 9537535Sdes#define HTTPCTYPELEN 59 9637535Sdes char content_type[HTTPCTYPELEN+1]; 9737535Sdes char *buf; 9837535Sdes int b_cur, eof; 9937535Sdes unsigned b_len, chunksize; 10037535Sdes}; 10137535Sdes 10237608Sdes/* 10337608Sdes * Send a formatted line; optionally echo to terminal 10437608Sdes */ 10537608Sdesstatic int 10637608Sdes_http_cmd(FILE *f, char *fmt, ...) 10737608Sdes{ 10837608Sdes va_list ap; 10937608Sdes 11037608Sdes va_start(ap, fmt); 11137608Sdes vfprintf(f, fmt, ap); 11262965Sdes DEBUG(fprintf(stderr, "\033[1m>>> ")); 11362965Sdes DEBUG(vfprintf(stderr, fmt, ap)); 11462965Sdes DEBUG(fprintf(stderr, "\033[m")); 11537608Sdes va_end(ap); 11637608Sdes 11737608Sdes return 0; /* XXX */ 11837608Sdes} 11937608Sdes 12037608Sdes/* 12137608Sdes * Fill the input buffer, do chunk decoding on the fly 12237608Sdes */ 12337535Sdesstatic char * 12437535Sdes_http_fillbuf(struct cookie *c) 12537535Sdes{ 12637535Sdes char *ln; 12737535Sdes unsigned int len; 12837535Sdes 12937535Sdes if (c->eof) 13037535Sdes return NULL; 13137535Sdes 13237535Sdes if (c->encoding == ENC_NONE) { 13337535Sdes c->buf = fgetln(c->real_f, &(c->b_len)); 13437535Sdes c->b_cur = 0; 13537535Sdes } else if (c->encoding == ENC_CHUNKED) { 13637535Sdes if (c->chunksize == 0) { 13737535Sdes ln = fgetln(c->real_f, &len); 13860707Sdes if (len <= 2) 13960707Sdes return NULL; 14037535Sdes DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: " 14137535Sdes "%*.*s\033[m\n", (int)len-2, (int)len-2, ln)); 14237535Sdes sscanf(ln, "%x", &(c->chunksize)); 14337535Sdes if (!c->chunksize) { 14437535Sdes DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 14537535Sdes "end of last chunk\033[m\n")); 14637535Sdes c->eof = 1; 14737535Sdes return NULL; 14837535Sdes } 14937535Sdes DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 15037535Sdes "new chunk: %X\033[m\n", c->chunksize)); 15137535Sdes } 15237535Sdes c->buf = fgetln(c->real_f, &(c->b_len)); 15337535Sdes if (c->b_len > c->chunksize) 15437535Sdes c->b_len = c->chunksize; 15537535Sdes c->chunksize -= c->b_len; 15637535Sdes c->b_cur = 0; 15737535Sdes } 15837535Sdes else return NULL; /* unknown encoding */ 15937535Sdes return c->buf; 16037535Sdes} 16137535Sdes 16237608Sdes/* 16337608Sdes * Read function 16437608Sdes */ 16537535Sdesstatic int 16637535Sdes_http_readfn(struct cookie *c, char *buf, int len) 16737535Sdes{ 16837535Sdes int l, pos = 0; 16937535Sdes while (len) { 17037535Sdes /* empty buffer */ 17137535Sdes if (!c->buf || (c->b_cur == c->b_len)) 17237535Sdes if (!_http_fillbuf(c)) 17337535Sdes break; 17437535Sdes 17537535Sdes l = c->b_len - c->b_cur; 17637535Sdes if (len < l) l = len; 17737535Sdes memcpy(buf + pos, c->buf + c->b_cur, l); 17837535Sdes c->b_cur += l; 17937535Sdes pos += l; 18037535Sdes len -= l; 18137535Sdes } 18237535Sdes 18337535Sdes if (ferror(c->real_f)) 18437535Sdes return -1; 18537535Sdes else return pos; 18637535Sdes} 18737535Sdes 18837608Sdes/* 18937608Sdes * Write function 19037608Sdes */ 19137535Sdesstatic int 19237535Sdes_http_writefn(struct cookie *c, const char *buf, int len) 19337535Sdes{ 19437535Sdes size_t r = fwrite(buf, 1, (size_t)len, c->real_f); 19537535Sdes return r ? r : -1; 19637535Sdes} 19737535Sdes 19837608Sdes/* 19937608Sdes * Close function 20037608Sdes */ 20137535Sdesstatic int 20237535Sdes_http_closefn(struct cookie *c) 20337535Sdes{ 20437535Sdes int r = fclose(c->real_f); 20537535Sdes free(c); 20637535Sdes return (r == EOF) ? -1 : 0; 20737535Sdes} 20837535Sdes 20937608Sdes/* 21037608Sdes * Extract content type from cookie 21137608Sdes */ 21237535Sdeschar * 21337535SdesfetchContentType(FILE *f) 21437535Sdes{ 21537535Sdes /* 21637535Sdes * We have no way of making sure this really *is* one of our cookies, 21737535Sdes * so just check for a null pointer and hope for the best. 21837535Sdes */ 21937535Sdes return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL; 22037535Sdes} 22137535Sdes 22237608Sdes/* 22337608Sdes * Base64 encoding 22437608Sdes */ 22562965Sdesstatic char * 22662965Sdes_http_base64(char *src) 22737608Sdes{ 22837608Sdes static const char base64[] = 22937608Sdes "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 23037608Sdes "abcdefghijklmnopqrstuvwxyz" 23137608Sdes "0123456789+/"; 23262965Sdes char *str, *dst; 23362965Sdes size_t l; 23462965Sdes int t, r; 23562965Sdes 23662965Sdes l = strlen(src); 23762965Sdes if ((str = malloc(((l + 2) / 3) * 4)) == NULL) 23862965Sdes return NULL; 23962965Sdes dst = str; 24062965Sdes r = 0; 24137608Sdes 24237608Sdes while (l >= 3) { 24337608Sdes t = (src[0] << 16) | (src[1] << 8) | src[2]; 24437608Sdes dst[0] = base64[(t >> 18) & 0x3f]; 24537608Sdes dst[1] = base64[(t >> 12) & 0x3f]; 24637608Sdes dst[2] = base64[(t >> 6) & 0x3f]; 24737608Sdes dst[3] = base64[(t >> 0) & 0x3f]; 24837608Sdes src += 3; l -= 3; 24937608Sdes dst += 4; r += 4; 25037608Sdes } 25137608Sdes 25237608Sdes switch (l) { 25337608Sdes case 2: 25437608Sdes t = (src[0] << 16) | (src[1] << 8); 25537608Sdes dst[0] = base64[(t >> 18) & 0x3f]; 25637608Sdes dst[1] = base64[(t >> 12) & 0x3f]; 25737608Sdes dst[2] = base64[(t >> 6) & 0x3f]; 25837608Sdes dst[3] = '='; 25937608Sdes dst += 4; 26037608Sdes r += 4; 26137608Sdes break; 26237608Sdes case 1: 26337608Sdes t = src[0] << 16; 26437608Sdes dst[0] = base64[(t >> 18) & 0x3f]; 26537608Sdes dst[1] = base64[(t >> 12) & 0x3f]; 26637608Sdes dst[2] = dst[3] = '='; 26737608Sdes dst += 4; 26837608Sdes r += 4; 26937608Sdes break; 27037608Sdes case 0: 27137608Sdes break; 27237608Sdes } 27337608Sdes 27437608Sdes *dst = 0; 27562965Sdes return str; 27637608Sdes} 27737608Sdes 27837608Sdes/* 27937608Sdes * Encode username and password 28037608Sdes */ 28162965Sdesstatic int 28262965Sdes_http_basic_auth(FILE *f, char *hdr, char *usr, char *pwd) 28337608Sdes{ 28462965Sdes char *upw, *auth; 28562965Sdes int r; 28637608Sdes 28762965Sdes if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 28862965Sdes return -1; 28962965Sdes auth = _http_base64(upw); 29062965Sdes free(upw); 29162965Sdes if (auth == NULL) 29262965Sdes return -1; 29362965Sdes r = _http_cmd(f, "%s: Basic %s" ENDL, hdr, auth); 29462965Sdes free(auth); 29562965Sdes return r; 29662965Sdes} 29762965Sdes 29862965Sdes/* 29962965Sdes * Send an authorization header 30062965Sdes */ 30162965Sdesstatic int 30262965Sdes_http_authorize(FILE *f, char *hdr, char *p) 30362965Sdes{ 30462965Sdes /* basic authorization */ 30562965Sdes if (strncasecmp(p, "basic:", 6) == 0) { 30662965Sdes char *user, *pwd, *str; 30762965Sdes int r; 30862965Sdes 30962965Sdes /* skip realm */ 31062965Sdes for (p += 6; *p && *p != ':'; ++p) 31162965Sdes /* nothing */ ; 31262965Sdes if (!*p || strchr(++p, ':') == NULL) 31362965Sdes return -1; 31462965Sdes if ((str = strdup(p)) == NULL) 31562965Sdes return -1; /* XXX */ 31662965Sdes user = str; 31762965Sdes pwd = strchr(str, ':'); 31862965Sdes *pwd++ = '\0'; 31962965Sdes r = _http_basic_auth(f, hdr, user, pwd); 32062965Sdes free(str); 32162965Sdes return r; 32262811Sdes } 32362965Sdes return -1; 32437608Sdes} 32537608Sdes 32637608Sdes/* 32760376Sdes * Connect to server or proxy 32837608Sdes */ 32962965Sdesstatic FILE * 33062965Sdes_http_connect(struct url *URL, char *flags, int *proxy) 33137535Sdes{ 33260376Sdes int direct, sd = -1, verbose; 33360737Sume#ifdef INET6 33460737Sume int af = AF_UNSPEC; 33560737Sume#else 33660737Sume int af = AF_INET; 33760737Sume#endif 33837535Sdes size_t len; 33960376Sdes char *px; 34060376Sdes FILE *f; 34160376Sdes 34255544Sdes direct = (flags && strchr(flags, 'd')); 34355544Sdes verbose = (flags && strchr(flags, 'v')); 34460737Sume if ((flags && strchr(flags, '4'))) 34560737Sume af = AF_INET; 34660737Sume else if ((flags && strchr(flags, '6'))) 34760737Sume af = AF_INET6; 34841862Sdes 34937535Sdes /* check port */ 35060189Sdes if (!URL->port) { 35160189Sdes struct servent *se; 35260189Sdes 35360587Sume if (strcasecmp(URL->scheme, "ftp") == 0) 35460587Sume if ((se = getservbyname("ftp", "tcp")) != NULL) 35560587Sume URL->port = ntohs(se->s_port); 35660587Sume else 35760587Sume URL->port = 21; 35860189Sdes else 35960587Sume if ((se = getservbyname("http", "tcp")) != NULL) 36060587Sume URL->port = ntohs(se->s_port); 36160587Sume else 36260587Sume URL->port = 80; 36360189Sdes } 36437535Sdes 36537535Sdes /* attempt to connect to proxy server */ 36655544Sdes if (!direct && (px = getenv("HTTP_PROXY")) != NULL) { 36741863Sdes char host[MAXHOSTNAMELEN]; 36860189Sdes int port = 0; 36937535Sdes 37037535Sdes /* measure length */ 37160737Sume#ifdef INET6 37260737Sume if (px[0] != '[' || 37360737Sume (len = strcspn(px, "]")) >= strlen(px) || 37460737Sume (px[++len] != '\0' && px[len] != ':')) 37560737Sume#endif 37660737Sume len = strcspn(px, ":"); 37737535Sdes 37855544Sdes /* get port (XXX atoi is a little too tolerant perhaps?) */ 37960189Sdes if (px[len] == ':') { 38060189Sdes if (strspn(px+len+1, "0123456789") != strlen(px+len+1) 38160189Sdes || strlen(px+len+1) > 5) { 38260189Sdes /* XXX we should emit some kind of warning */ 38360189Sdes } 38437535Sdes port = atoi(px+len+1); 38560189Sdes if (port < 1 || port > 65535) { 38660189Sdes /* XXX we should emit some kind of warning */ 38760189Sdes } 38860189Sdes } 38960189Sdes if (!port) { 39060189Sdes#if 0 39160189Sdes /* 39260189Sdes * commented out, since there is currently no service name 39360189Sdes * for HTTP proxies 39460189Sdes */ 39560189Sdes struct servent *se; 39660189Sdes 39760189Sdes if ((se = getservbyname("xxxx", "tcp")) != NULL) 39860189Sdes port = ntohs(se->s_port); 39960189Sdes else 40060189Sdes#endif 40160189Sdes port = 3128; 40260189Sdes } 40337535Sdes 40437535Sdes /* get host name */ 40560737Sume#ifdef INET6 40660737Sume if (len > 1 && px[0] == '[' && px[len - 1] == ']') { 40760737Sume px++; 40860737Sume len -= 2; 40960737Sume } 41060737Sume#endif 41137535Sdes if (len >= MAXHOSTNAMELEN) 41237535Sdes len = MAXHOSTNAMELEN - 1; 41337535Sdes strncpy(host, px, len); 41437535Sdes host[len] = 0; 41537535Sdes 41637535Sdes /* connect */ 41760737Sume sd = _fetch_connect(host, port, af, verbose); 41837535Sdes } 41937535Sdes 42037535Sdes /* if no proxy is configured or could be contacted, try direct */ 42162965Sdes *proxy = (sd != -1); 42262965Sdes if (!*proxy) { 42360587Sume if (strcasecmp(URL->scheme, "ftp") == 0) 42460587Sume goto ouch; 42560737Sume if ((sd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1) 42637535Sdes goto ouch; 42737535Sdes } 42837535Sdes 42937535Sdes /* reopen as stream */ 43037571Sdes if ((f = fdopen(sd, "r+")) == NULL) 43137535Sdes goto ouch; 43260376Sdes 43360376Sdes return f; 43437535Sdes 43560376Sdesouch: 43660376Sdes if (sd >= 0) 43760376Sdes close(sd); 43860376Sdes _http_seterr(999); /* XXX do this properly RSN */ 43960376Sdes return NULL; 44060376Sdes} 44160376Sdes 44260376Sdes/* 44360954Sdes * Check a header line 44460954Sdes */ 44562965Sdesstatic char * 44660954Sdes_http_match(char *str, char *hdr) 44760954Sdes{ 44860954Sdes while (*str && *hdr && tolower(*str++) == tolower(*hdr++)) 44960954Sdes /* nothing */; 45060954Sdes if (*str || *hdr != ':') 45160954Sdes return NULL; 45260954Sdes while (*hdr && isspace(*++hdr)) 45360954Sdes /* nothing */; 45460954Sdes return hdr; 45560954Sdes} 45660954Sdes 45760954Sdes/* 45860376Sdes * Send a HEAD or GET request 45960376Sdes */ 46062965Sdesstatic int 46162965Sdes_http_request(FILE *f, char *op, struct url *URL, char *flags, int proxy) 46260376Sdes{ 46360376Sdes int e, verbose; 46460376Sdes char *ln, *p; 46560376Sdes size_t len; 46660737Sume char *host; 46760737Sume#ifdef INET6 46860737Sume char hbuf[MAXHOSTNAMELEN + 1]; 46960737Sume#endif 47060376Sdes 47160376Sdes verbose = (flags && strchr(flags, 'v')); 47260737Sume 47360737Sume host = URL->host; 47460737Sume#ifdef INET6 47560737Sume if (strchr(URL->host, ':')) { 47660737Sume snprintf(hbuf, sizeof(hbuf), "[%s]", URL->host); 47760737Sume host = hbuf; 47860737Sume } 47960737Sume#endif 48060376Sdes 48162965Sdes /* send request (proxies require absolute form) */ 48241862Sdes if (verbose) 48360587Sume _fetch_info("requesting %s://%s:%d%s", 48460737Sume URL->scheme, host, URL->port, URL->doc); 48562965Sdes if (proxy) 48662965Sdes _http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL, 48762965Sdes op, URL->scheme, host, URL->port, URL->doc); 48862965Sdes else 48962965Sdes _http_cmd(f, "%s %s HTTP/1.1" ENDL, op, URL->doc); 49037535Sdes 49137535Sdes /* start sending headers away */ 49262965Sdes if (URL->user[0] || URL->pwd[0]) 49362965Sdes _http_basic_auth(f, "Authorization", 49462965Sdes URL->user ? URL->user : "", 49562965Sdes URL->pwd ? URL->pwd : ""); 49662965Sdes else if ((p = getenv("HTTP_AUTH")) != NULL) 49762965Sdes _http_authorize(f, "Authorization", p); 49862965Sdes if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL) 49962965Sdes _http_authorize(f, "Proxy-Authorization", p); 50060737Sume _http_cmd(f, "Host: %s:%d" ENDL, host, URL->port); 50137608Sdes _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname); 50260196Sdes if (URL->offset) 50360196Sdes _http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset); 50437608Sdes _http_cmd(f, "Connection: close" ENDL ENDL); 50537535Sdes 50637535Sdes /* get response */ 50737535Sdes if ((ln = fgetln(f, &len)) == NULL) 50860376Sdes return 999; 50937535Sdes DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n", 51037535Sdes (int)len-2, (int)len-2, ln)); 51137535Sdes 51237535Sdes /* we can't use strchr() and friends since ln isn't NUL-terminated */ 51337535Sdes p = ln; 51437535Sdes while ((p < ln + len) && !isspace(*p)) 51537535Sdes p++; 51637535Sdes while ((p < ln + len) && !isdigit(*p)) 51737535Sdes p++; 51837535Sdes if (!isdigit(*p)) 51960376Sdes return 999; 52060376Sdes 52141863Sdes e = atoi(p); 52241863Sdes DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", e)); 52360376Sdes return e; 52460376Sdes} 52560376Sdes 52660376Sdes/* 52760376Sdes * Retrieve a file by HTTP 52860376Sdes */ 52960376SdesFILE * 53060376SdesfetchGetHTTP(struct url *URL, char *flags) 53160376Sdes{ 53262965Sdes int e, enc = ENC_NONE, i, noredirect, proxy; 53360376Sdes struct cookie *c; 53460376Sdes char *ln, *p, *q; 53560376Sdes FILE *f, *cf; 53660376Sdes size_t len; 53760376Sdes off_t pos = 0; 53860376Sdes 53960954Sdes noredirect = (flags && strchr(flags, 'A')); 54060954Sdes 54160376Sdes /* allocate cookie */ 54260376Sdes if ((c = calloc(1, sizeof *c)) == NULL) 54360376Sdes return NULL; 54460376Sdes 54560376Sdes /* connect */ 54662965Sdes if ((f = _http_connect(URL, flags, &proxy)) == NULL) { 54760376Sdes free(c); 54860376Sdes return NULL; 54960376Sdes } 55060376Sdes c->real_f = f; 55160376Sdes 55262965Sdes e = _http_request(f, "GET", URL, flags, proxy); 55360954Sdes if (e != (URL->offset ? HTTP_PARTIAL : HTTP_OK) 55460954Sdes && (e != HTTP_MOVED || noredirect)) { 55541863Sdes _http_seterr(e); 55661896Sdes free(c); 55761896Sdes fclose(f); 55861896Sdes return NULL; 55937571Sdes } 56037535Sdes 56137535Sdes /* browse through header */ 56237535Sdes while (1) { 56337535Sdes if ((ln = fgetln(f, &len)) == NULL) 56437535Sdes goto fouch; 56537535Sdes if ((ln[0] == '\r') || (ln[0] == '\n')) 56637535Sdes break; 56760376Sdes while (isspace(ln[len-1])) 56860376Sdes --len; 56960376Sdes ln[len] = '\0'; /* XXX */ 57060376Sdes DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 57160954Sdes if ((p = _http_match("Location", ln)) != NULL) { 57260954Sdes struct url *url; 57360954Sdes 57460376Sdes for (q = p; *q && !isspace(*q); q++) 57537535Sdes /* VOID */ ; 57637535Sdes *q = 0; 57760954Sdes if ((url = fetchParseURL(p)) == NULL) 57860954Sdes goto fouch; 57960954Sdes url->offset = URL->offset; 58060954Sdes url->length = URL->length; 58160954Sdes DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 58260954Sdes cf = fetchGetHTTP(url, flags); 58360954Sdes fetchFreeURL(url); 58460954Sdes fclose(f); 58560954Sdes return cf; 58660954Sdes } else if ((p = _http_match("Transfer-Encoding", ln)) != NULL) { 58760954Sdes for (q = p; *q && !isspace(*q); q++) 58860954Sdes /* VOID */ ; 58960954Sdes *q = 0; 59037535Sdes if (strcasecmp(p, "chunked") == 0) 59137535Sdes enc = ENC_CHUNKED; 59260376Sdes DEBUG(fprintf(stderr, "transfer encoding: [\033[1m%s\033[m]\n", p)); 59360376Sdes } else if ((p = _http_match("Content-Type", ln)) != NULL) { 59460376Sdes for (i = 0; *p && i < HTTPCTYPELEN; p++, i++) 59560376Sdes c->content_type[i] = *p; 59637535Sdes do c->content_type[i--] = 0; while (isspace(c->content_type[i])); 59760376Sdes DEBUG(fprintf(stderr, "content type: [\033[1m%s\033[m]\n", 59837535Sdes c->content_type)); 59960376Sdes } else if ((p = _http_match("Content-Range", ln)) != NULL) { 60060376Sdes if (strncasecmp(p, "bytes ", 6) != 0) 60160196Sdes goto fouch; 60260376Sdes p += 6; 60360376Sdes while (*p && isdigit(*p)) 60460196Sdes pos = pos * 10 + (*p++ - '0'); 60560196Sdes /* XXX wouldn't hurt to be slightly more paranoid here */ 60660376Sdes DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", pos)); 60760196Sdes if (pos > URL->offset) 60860196Sdes goto fouch; 60937535Sdes } 61037535Sdes } 61137535Sdes 61237535Sdes /* only body remains */ 61337535Sdes c->encoding = enc; 61437535Sdes cf = funopen(c, 61537535Sdes (int (*)(void *, char *, int))_http_readfn, 61637535Sdes (int (*)(void *, const char *, int))_http_writefn, 61737535Sdes (fpos_t (*)(void *, fpos_t, int))NULL, 61837535Sdes (int (*)(void *))_http_closefn); 61937535Sdes if (cf == NULL) 62037535Sdes goto fouch; 62160189Sdes 62260196Sdes while (pos < URL->offset) 62360196Sdes if (fgetc(cf) == EOF) 62460196Sdes goto cfouch; 62560196Sdes 62637535Sdes return cf; 62737535Sdes 62837535Sdesfouch: 62937535Sdes fclose(f); 63037535Sdes free(c); 63141862Sdes _http_seterr(999); /* XXX do this properly RSN */ 63237535Sdes return NULL; 63360196Sdescfouch: 63460196Sdes fclose(cf); 63560196Sdes _http_seterr(999); /* XXX do this properly RSN */ 63660196Sdes return NULL; 63737535Sdes} 63837535Sdes 63937535SdesFILE * 64040975SdesfetchPutHTTP(struct url *URL, char *flags) 64137535Sdes{ 64237535Sdes warnx("fetchPutHTTP(): not implemented"); 64337535Sdes return NULL; 64437535Sdes} 64540975Sdes 64640975Sdes/* 64740975Sdes * Get an HTTP document's metadata 64840975Sdes */ 64940975Sdesint 65060376SdesfetchStatHTTP(struct url *URL, struct url_stat *us, char *flags) 65140975Sdes{ 65262965Sdes int e, noredirect, proxy; 65360376Sdes size_t len; 65460954Sdes char *ln, *p, *q; 65560376Sdes FILE *f; 65660954Sdes 65760954Sdes noredirect = (flags && strchr(flags, 'A')); 65860376Sdes 65960581Sdes us->size = -1; 66060581Sdes us->atime = us->mtime = 0; 66160376Sdes 66260376Sdes /* connect */ 66362965Sdes if ((f = _http_connect(URL, flags, &proxy)) == NULL) 66460376Sdes return -1; 66560376Sdes 66662965Sdes e = _http_request(f, "HEAD", URL, flags, proxy); 66760954Sdes if (e != HTTP_OK && (e != HTTP_MOVED || noredirect)) { 66860376Sdes _http_seterr(e); 66961896Sdes fclose(f); 67061896Sdes return -1; 67160376Sdes } 67260376Sdes 67360376Sdes while (1) { 67460376Sdes if ((ln = fgetln(f, &len)) == NULL) 67560376Sdes goto fouch; 67660376Sdes if ((ln[0] == '\r') || (ln[0] == '\n')) 67760376Sdes break; 67860376Sdes while (isspace(ln[len-1])) 67960376Sdes --len; 68060376Sdes ln[len] = '\0'; /* XXX */ 68160376Sdes DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 68260954Sdes if ((p = _http_match("Location", ln)) != NULL) { 68360954Sdes struct url *url; 68460954Sdes 68560954Sdes for (q = p; *q && !isspace(*q); q++) 68660954Sdes /* VOID */ ; 68760954Sdes *q = 0; 68860954Sdes if ((url = fetchParseURL(p)) == NULL) 68960954Sdes goto ouch; 69060954Sdes url->offset = URL->offset; 69160954Sdes url->length = URL->length; 69260954Sdes DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 69360954Sdes e = fetchStatHTTP(url, us, flags); 69460954Sdes fetchFreeURL(url); 69560954Sdes fclose(f); 69660954Sdes return e; 69760954Sdes } else if ((p = _http_match("Last-Modified", ln)) != NULL) { 69860376Sdes struct tm tm; 69960376Sdes char locale[64]; 70060376Sdes 70160376Sdes strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale); 70260376Sdes setlocale(LC_TIME, "C"); 70360376Sdes strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 70460376Sdes /* XXX should add support for date-2 and date-3 */ 70560376Sdes setlocale(LC_TIME, locale); 70660376Sdes us->atime = us->mtime = timegm(&tm); 70760376Sdes DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 70860376Sdes "%02d:%02d:%02d\033[m]\n", 70960376Sdes tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 71060376Sdes tm.tm_hour, tm.tm_min, tm.tm_sec)); 71160376Sdes } else if ((p = _http_match("Content-Length", ln)) != NULL) { 71260376Sdes us->size = 0; 71360376Sdes while (*p && isdigit(*p)) 71460376Sdes us->size = us->size * 10 + (*p++ - '0'); 71560376Sdes DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", us->size)); 71660376Sdes } 71760376Sdes } 71860581Sdes 71960581Sdes fclose(f); 72060376Sdes return 0; 72160376Sdes ouch: 72260376Sdes _http_seterr(999); /* XXX do this properly RSN */ 72360376Sdes fouch: 72460376Sdes fclose(f); 72560376Sdes return -1; 72640975Sdes} 72741989Sdes 72841989Sdes/* 72941989Sdes * List a directory 73041989Sdes */ 73141989Sdesstruct url_ent * 73241989SdesfetchListHTTP(struct url *url, char *flags) 73341989Sdes{ 73441989Sdes warnx("fetchListHTTP(): not implemented"); 73541989Sdes return NULL; 73641989Sdes} 737