http.c revision 62811
1139825Simp/*- 299657Sbenno * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 378342Sbenno * All rights reserved. 478342Sbenno * 578342Sbenno * Redistribution and use in source and binary forms, with or without 678342Sbenno * modification, are permitted provided that the following conditions 778342Sbenno * are met: 878342Sbenno * 1. Redistributions of source code must retain the above copyright 999657Sbenno * notice, this list of conditions and the following disclaimer 1099657Sbenno * in this position and unchanged. 1199657Sbenno * 2. Redistributions in binary form must reproduce the above copyright 1299657Sbenno * notice, this list of conditions and the following disclaimer in the 1378342Sbenno * documentation and/or other materials provided with the distribution. 1499657Sbenno * 3. The name of the author may not be used to endorse or promote products 1599657Sbenno * derived from this software without specific prior written permission 1699657Sbenno * 1799657Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1899657Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1999657Sbenno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2099657Sbenno * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2199657Sbenno * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2299657Sbenno * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2399657Sbenno * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2499657Sbenno * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2578342Sbenno * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2678342Sbenno * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27209812Snwhitehorn * 28209812Snwhitehorn * $FreeBSD: head/lib/libfetch/http.c 62811 2000-07-08 08:08:58Z des $ 29209812Snwhitehorn */ 30209812Snwhitehorn 31113038Sobrien/* 32113038Sobrien * The base64 code in this file is based on code from MIT fetch, which 3378342Sbenno * has the following copyright and license: 3499657Sbenno * 3599657Sbenno *- 3699657Sbenno * Copyright 1997 Massachusetts Institute of Technology 3799657Sbenno * 3899657Sbenno * Permission to use, copy, modify, and distribute this software and 39209812Snwhitehorn * its documentation for any purpose and without fee is hereby 40209812Snwhitehorn * granted, provided that both the above copyright notice and this 4199657Sbenno * permission notice appear in all copies, that both the above 4299657Sbenno * copyright notice and this permission notice appear in all 43246713Skib * supporting documentation, and that the name of M.I.T. not be used 4499657Sbenno * in advertising or publicity pertaining to distribution of the 45246713Skib * software without specific, written prior permission. M.I.T. makes 46108939Sgrehan * no representations about the suitability of this software for any 4799657Sbenno * purpose. It is provided "as is" without express or implied 4899657Sbenno * warranty. 49239008Sjhb * 50239008Sjhb * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 5199657Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 52108939Sgrehan * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 5399657Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 54112436Smux * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 5599657Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 56229967Snwhitehorn * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 57209812Snwhitehorn * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 5899657Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 59216154Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 60209812Snwhitehorn * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61216154Snwhitehorn * SUCH DAMAGE. */ 62216154Snwhitehorn 63209812Snwhitehorn#include <sys/param.h> 64209812Snwhitehorn#include <sys/socket.h> 6599657Sbenno 66209812Snwhitehorn#include <err.h> 67209812Snwhitehorn#include <ctype.h> 68232356Sjhb#include <locale.h> 69209812Snwhitehorn#include <netdb.h> 70209812Snwhitehorn#include <stdarg.h> 7199657Sbenno#include <stdio.h> 72209812Snwhitehorn#include <stdlib.h> 73209812Snwhitehorn#include <string.h> 74209812Snwhitehorn#include <time.h> 75209812Snwhitehorn#include <unistd.h> 76209812Snwhitehorn 77209812Snwhitehorn#include "fetch.h" 78209812Snwhitehorn#include "common.h" 79117126Sscottl#include "httperr.h" 80117126Sscottl 81209812Snwhitehornextern char *__progname; 82216154Snwhitehorn 83216154Snwhitehorn#define ENDL "\r\n" 8499657Sbenno 8599657Sbenno#define HTTP_OK 200 86209812Snwhitehorn#define HTTP_PARTIAL 206 87209812Snwhitehorn#define HTTP_MOVED 302 88209812Snwhitehorn 89209812Snwhitehornstruct cookie 90246713Skib{ 91209812Snwhitehorn FILE *real_f; 92209812Snwhitehorn#define ENC_NONE 0 93209812Snwhitehorn#define ENC_CHUNKED 1 94209812Snwhitehorn int encoding; /* 1 = chunked, 0 = none */ 95209812Snwhitehorn#define HTTPCTYPELEN 59 96209812Snwhitehorn char content_type[HTTPCTYPELEN+1]; 97209812Snwhitehorn char *buf; 98209812Snwhitehorn int b_cur, eof; 99209812Snwhitehorn unsigned b_len, chunksize; 100209812Snwhitehorn}; 101209812Snwhitehorn 102209812Snwhitehorn/* 103209812Snwhitehorn * Send a formatted line; optionally echo to terminal 104209812Snwhitehorn */ 105209812Snwhitehornstatic int 106209812Snwhitehorn_http_cmd(FILE *f, char *fmt, ...) 107209812Snwhitehorn{ 108209812Snwhitehorn va_list ap; 109209812Snwhitehorn 110209812Snwhitehorn va_start(ap, fmt); 111209812Snwhitehorn vfprintf(f, fmt, ap); 112209812Snwhitehorn#ifndef NDEBUG 113209812Snwhitehorn fprintf(stderr, "\033[1m>>> "); 114209812Snwhitehorn vfprintf(stderr, fmt, ap); 115209812Snwhitehorn fprintf(stderr, "\033[m"); 116209812Snwhitehorn#endif 117209812Snwhitehorn va_end(ap); 118209812Snwhitehorn 119209812Snwhitehorn return 0; /* XXX */ 120227309Sed} 121209812Snwhitehorn 122209812Snwhitehorn/* 123209812Snwhitehorn * Fill the input buffer, do chunk decoding on the fly 12499657Sbenno */ 125209812Snwhitehornstatic char * 126209812Snwhitehorn_http_fillbuf(struct cookie *c) 127209812Snwhitehorn{ 128209812Snwhitehorn char *ln; 129246713Skib unsigned int len; 130216154Snwhitehorn 131216154Snwhitehorn if (c->eof) 132209812Snwhitehorn return NULL; 133209812Snwhitehorn 134209812Snwhitehorn if (c->encoding == ENC_NONE) { 135239008Sjhb c->buf = fgetln(c->real_f, &(c->b_len)); 13699657Sbenno c->b_cur = 0; 13799657Sbenno } else if (c->encoding == ENC_CHUNKED) { 138209812Snwhitehorn if (c->chunksize == 0) { 139209812Snwhitehorn ln = fgetln(c->real_f, &len); 140209812Snwhitehorn if (len <= 2) 141209812Snwhitehorn return NULL; 142209812Snwhitehorn DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: " 143209812Snwhitehorn "%*.*s\033[m\n", (int)len-2, (int)len-2, ln)); 144209812Snwhitehorn sscanf(ln, "%x", &(c->chunksize)); 145209812Snwhitehorn if (!c->chunksize) { 146209812Snwhitehorn DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 147246713Skib "end of last chunk\033[m\n")); 148246713Skib c->eof = 1; 149209812Snwhitehorn return NULL; 150209812Snwhitehorn } 151209812Snwhitehorn DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 15299657Sbenno "new chunk: %X\033[m\n", c->chunksize)); 153209812Snwhitehorn } 154209812Snwhitehorn c->buf = fgetln(c->real_f, &(c->b_len)); 155209812Snwhitehorn if (c->b_len > c->chunksize) 156209812Snwhitehorn c->b_len = c->chunksize; 157209812Snwhitehorn c->chunksize -= c->b_len; 158209812Snwhitehorn c->b_cur = 0; 159209812Snwhitehorn } 160209812Snwhitehorn else return NULL; /* unknown encoding */ 161209812Snwhitehorn return c->buf; 162209812Snwhitehorn} 163209812Snwhitehorn 164209812Snwhitehorn/* 165209812Snwhitehorn * Read function 166209812Snwhitehorn */ 167209812Snwhitehornstatic int 168216154Snwhitehorn_http_readfn(struct cookie *c, char *buf, int len) 169216154Snwhitehorn{ 170209812Snwhitehorn int l, pos = 0; 171216154Snwhitehorn while (len) { 172216154Snwhitehorn /* empty buffer */ 173216154Snwhitehorn if (!c->buf || (c->b_cur == c->b_len)) 174216154Snwhitehorn if (!_http_fillbuf(c)) 175216154Snwhitehorn break; 176216154Snwhitehorn 177209812Snwhitehorn l = c->b_len - c->b_cur; 178209812Snwhitehorn if (len < l) l = len; 179209812Snwhitehorn memcpy(buf + pos, c->buf + c->b_cur, l); 180209812Snwhitehorn c->b_cur += l; 181209812Snwhitehorn pos += l; 182209812Snwhitehorn len -= l; 183209812Snwhitehorn } 184117126Sscottl 185117126Sscottl if (ferror(c->real_f)) 186117126Sscottl return -1; 187117126Sscottl else return pos; 188117126Sscottl} 189117126Sscottl 190117126Sscottl/* 191117126Sscottl * Write function 192117126Sscottl */ 193117126Sscottlstatic int 194117126Sscottl_http_writefn(struct cookie *c, const char *buf, int len) 195117126Sscottl{ 196117126Sscottl size_t r = fwrite(buf, 1, (size_t)len, c->real_f); 197117126Sscottl return r ? r : -1; 198117126Sscottl} 199117126Sscottl 200117126Sscottl/* 201117126Sscottl * Close function 202117126Sscottl */ 203117126Sscottlstatic int 204117126Sscottl_http_closefn(struct cookie *c) 205117126Sscottl{ 206117126Sscottl int r = fclose(c->real_f); 207117126Sscottl free(c); 208117126Sscottl return (r == EOF) ? -1 : 0; 209117126Sscottl} 210117126Sscottl 211117126Sscottl/* 212117126Sscottl * Extract content type from cookie 213117126Sscottl */ 214117126Sscottlchar * 215117126SscottlfetchContentType(FILE *f) 216117126Sscottl{ 217117126Sscottl /* 218117126Sscottl * We have no way of making sure this really *is* one of our cookies, 219209812Snwhitehorn * so just check for a null pointer and hope for the best. 220209812Snwhitehorn */ 221117126Sscottl return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL; 22299657Sbenno} 22399657Sbenno 22499657Sbenno/* 22599657Sbenno * Base64 encoding 226232356Sjhb */ 227209812Snwhitehornint 228209812Snwhitehorn_http_base64(char *dst, char *src, int l) 229209812Snwhitehorn{ 230209812Snwhitehorn static const char base64[] = 23199657Sbenno "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 23299657Sbenno "abcdefghijklmnopqrstuvwxyz" 23399657Sbenno "0123456789+/"; 23499657Sbenno int t, r = 0; 235209812Snwhitehorn 236209812Snwhitehorn while (l >= 3) { 237209812Snwhitehorn t = (src[0] << 16) | (src[1] << 8) | src[2]; 238209812Snwhitehorn dst[0] = base64[(t >> 18) & 0x3f]; 239209812Snwhitehorn dst[1] = base64[(t >> 12) & 0x3f]; 240209812Snwhitehorn dst[2] = base64[(t >> 6) & 0x3f]; 241209812Snwhitehorn dst[3] = base64[(t >> 0) & 0x3f]; 242209812Snwhitehorn src += 3; l -= 3; 24399657Sbenno dst += 4; r += 4; 24499657Sbenno } 24599657Sbenno 246209812Snwhitehorn switch (l) { 247209812Snwhitehorn case 2: 248209812Snwhitehorn t = (src[0] << 16) | (src[1] << 8); 249209812Snwhitehorn dst[0] = base64[(t >> 18) & 0x3f]; 250209812Snwhitehorn dst[1] = base64[(t >> 12) & 0x3f]; 25199657Sbenno dst[2] = base64[(t >> 6) & 0x3f]; 252209812Snwhitehorn dst[3] = '='; 25399657Sbenno dst += 4; 25499657Sbenno r += 4; 25599657Sbenno break; 25699657Sbenno case 1: 257209812Snwhitehorn t = src[0] << 16; 258209812Snwhitehorn dst[0] = base64[(t >> 18) & 0x3f]; 25999657Sbenno dst[1] = base64[(t >> 12) & 0x3f]; 26099657Sbenno dst[2] = dst[3] = '='; 261209812Snwhitehorn dst += 4; 262209812Snwhitehorn r += 4; 26399657Sbenno break; 26499657Sbenno case 0: 26599657Sbenno break; 26699657Sbenno } 267117126Sscottl 268117126Sscottl *dst = 0; 269117126Sscottl return r; 270117126Sscottl} 271117126Sscottl 272117126Sscottl/* 273117126Sscottl * Encode username and password 27499657Sbenno */ 275209812Snwhitehornchar * 276209812Snwhitehorn_http_auth(char *usr, char *pwd) 277209812Snwhitehorn{ 278209812Snwhitehorn int len, lup; 279134934Sscottl char *uandp, *str = NULL; 280134934Sscottl 281134934Sscottl lup = strlen(usr) + 1 + strlen(pwd);/* length of "usr:pwd" */ 282209812Snwhitehorn uandp = (char*)malloc(lup + 1); 283134934Sscottl if (uandp) { 284209812Snwhitehorn len = ((lup + 2) / 3) * 4; /* length of base64 encoded "usr:pwd" incl. padding */ 285209812Snwhitehorn str = (char*)malloc(len + 1); 286209812Snwhitehorn if (str) { 287209812Snwhitehorn strcpy(uandp, usr); 288209812Snwhitehorn strcat(uandp, ":"); 289209812Snwhitehorn strcat(uandp, pwd); 290209812Snwhitehorn _http_base64(str, uandp, lup); 291209812Snwhitehorn } 29299657Sbenno free(uandp); 293112436Smux } 294112436Smux return str; 295216154Snwhitehorn} 296216154Snwhitehorn 29799657Sbenno/* 29899657Sbenno * Connect to server or proxy 299216154Snwhitehorn */ 300209812SnwhitehornFILE * 301209812Snwhitehorn_http_connect(struct url *URL, char *flags) 302216154Snwhitehorn{ 303216154Snwhitehorn int direct, sd = -1, verbose; 304216154Snwhitehorn#ifdef INET6 305209812Snwhitehorn int af = AF_UNSPEC; 306209812Snwhitehorn#else 307209812Snwhitehorn int af = AF_INET; 308209812Snwhitehorn#endif 309209812Snwhitehorn size_t len; 310209812Snwhitehorn char *px; 311209812Snwhitehorn FILE *f; 312209812Snwhitehorn 313209812Snwhitehorn direct = (flags && strchr(flags, 'd')); 314209812Snwhitehorn verbose = (flags && strchr(flags, 'v')); 315209812Snwhitehorn if ((flags && strchr(flags, '4'))) 316209812Snwhitehorn af = AF_INET; 317209812Snwhitehorn else if ((flags && strchr(flags, '6'))) 318209812Snwhitehorn af = AF_INET6; 319209812Snwhitehorn 320209812Snwhitehorn /* check port */ 321209812Snwhitehorn if (!URL->port) { 322209812Snwhitehorn struct servent *se; 323209812Snwhitehorn 324209812Snwhitehorn if (strcasecmp(URL->scheme, "ftp") == 0) 325209812Snwhitehorn if ((se = getservbyname("ftp", "tcp")) != NULL) 326209812Snwhitehorn URL->port = ntohs(se->s_port); 327209812Snwhitehorn else 328209812Snwhitehorn URL->port = 21; 329209812Snwhitehorn else 330209812Snwhitehorn if ((se = getservbyname("http", "tcp")) != NULL) 331209812Snwhitehorn URL->port = ntohs(se->s_port); 332209812Snwhitehorn else 333209812Snwhitehorn URL->port = 80; 334209812Snwhitehorn } 335209812Snwhitehorn 336209812Snwhitehorn /* attempt to connect to proxy server */ 33799657Sbenno if (!direct && (px = getenv("HTTP_PROXY")) != NULL) { 33899657Sbenno char host[MAXHOSTNAMELEN]; 33999657Sbenno int port = 0; 34099657Sbenno 34199657Sbenno /* measure length */ 34299657Sbenno#ifdef INET6 343209812Snwhitehorn if (px[0] != '[' || 344209812Snwhitehorn (len = strcspn(px, "]")) >= strlen(px) || 345209812Snwhitehorn (px[++len] != '\0' && px[len] != ':')) 346209812Snwhitehorn#endif 347209812Snwhitehorn len = strcspn(px, ":"); 348209812Snwhitehorn 34999657Sbenno /* get port (XXX atoi is a little too tolerant perhaps?) */ 350209812Snwhitehorn if (px[len] == ':') { 351209812Snwhitehorn if (strspn(px+len+1, "0123456789") != strlen(px+len+1) 352209812Snwhitehorn || strlen(px+len+1) > 5) { 353209812Snwhitehorn /* XXX we should emit some kind of warning */ 354209812Snwhitehorn } 355209812Snwhitehorn port = atoi(px+len+1); 356209812Snwhitehorn if (port < 1 || port > 65535) { 357209812Snwhitehorn /* XXX we should emit some kind of warning */ 358209812Snwhitehorn } 359209812Snwhitehorn } 360209812Snwhitehorn if (!port) { 361209812Snwhitehorn#if 0 362209812Snwhitehorn /* 363209812Snwhitehorn * commented out, since there is currently no service name 364209812Snwhitehorn * for HTTP proxies 365209812Snwhitehorn */ 366209812Snwhitehorn struct servent *se; 367209812Snwhitehorn 368209812Snwhitehorn if ((se = getservbyname("xxxx", "tcp")) != NULL) 369209812Snwhitehorn port = ntohs(se->s_port); 370209812Snwhitehorn else 371209812Snwhitehorn#endif 372209812Snwhitehorn port = 3128; 373209812Snwhitehorn } 374209812Snwhitehorn 375209812Snwhitehorn /* get host name */ 37699657Sbenno#ifdef INET6 37799657Sbenno if (len > 1 && px[0] == '[' && px[len - 1] == ']') { 37899657Sbenno px++; 37999657Sbenno len -= 2; 38099657Sbenno } 38199657Sbenno#endif 38299657Sbenno if (len >= MAXHOSTNAMELEN) 38399657Sbenno len = MAXHOSTNAMELEN - 1; 38499657Sbenno strncpy(host, px, len); 385209812Snwhitehorn host[len] = 0; 38699657Sbenno 387209812Snwhitehorn /* connect */ 388209812Snwhitehorn sd = _fetch_connect(host, port, af, verbose); 389216154Snwhitehorn } 390216154Snwhitehorn 391216154Snwhitehorn /* if no proxy is configured or could be contacted, try direct */ 392216154Snwhitehorn if (sd == -1) { 393216154Snwhitehorn if (strcasecmp(URL->scheme, "ftp") == 0) 394216154Snwhitehorn goto ouch; 395209812Snwhitehorn if ((sd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1) 396209812Snwhitehorn goto ouch; 397216154Snwhitehorn } 398209812Snwhitehorn 399209812Snwhitehorn /* reopen as stream */ 400209812Snwhitehorn if ((f = fdopen(sd, "r+")) == NULL) 401209812Snwhitehorn goto ouch; 402209812Snwhitehorn 403209812Snwhitehorn return f; 404209812Snwhitehorn 405209812Snwhitehornouch: 406209812Snwhitehorn if (sd >= 0) 407209812Snwhitehorn close(sd); 408209812Snwhitehorn _http_seterr(999); /* XXX do this properly RSN */ 409209812Snwhitehorn return NULL; 410209812Snwhitehorn} 411209812Snwhitehorn 412209812Snwhitehorn/* 413209812Snwhitehorn * Check a header line 414209812Snwhitehorn */ 415209812Snwhitehornchar * 416209812Snwhitehorn_http_match(char *str, char *hdr) 417209812Snwhitehorn{ 418209812Snwhitehorn while (*str && *hdr && tolower(*str++) == tolower(*hdr++)) 419209812Snwhitehorn /* nothing */; 420209812Snwhitehorn if (*str || *hdr != ':') 421209812Snwhitehorn return NULL; 422209812Snwhitehorn while (*hdr && isspace(*++hdr)) 423209812Snwhitehorn /* nothing */; 424209812Snwhitehorn return hdr; 425209812Snwhitehorn} 426209812Snwhitehorn 427209812Snwhitehorn/* 428209812Snwhitehorn * Send a HEAD or GET request 429209812Snwhitehorn */ 430209812Snwhitehornint 431209812Snwhitehorn_http_request(FILE *f, char *op, struct url *URL, char *flags) 432209812Snwhitehorn{ 433209812Snwhitehorn int e, verbose; 434209812Snwhitehorn char *ln, *p; 435209812Snwhitehorn size_t len; 436209812Snwhitehorn char *host; 437209812Snwhitehorn#ifdef INET6 438209812Snwhitehorn char hbuf[MAXHOSTNAMELEN + 1]; 439209812Snwhitehorn#endif 440209812Snwhitehorn 441209812Snwhitehorn verbose = (flags && strchr(flags, 'v')); 442209812Snwhitehorn 443209812Snwhitehorn host = URL->host; 444209812Snwhitehorn#ifdef INET6 445216154Snwhitehorn if (strchr(URL->host, ':')) { 446216154Snwhitehorn snprintf(hbuf, sizeof(hbuf), "[%s]", URL->host); 447216154Snwhitehorn host = hbuf; 448216154Snwhitehorn } 449216154Snwhitehorn#endif 450216154Snwhitehorn 451216154Snwhitehorn /* send request (proxies require absolute form, so use that) */ 452216154Snwhitehorn if (verbose) 453216154Snwhitehorn _fetch_info("requesting %s://%s:%d%s", 454216154Snwhitehorn URL->scheme, host, URL->port, URL->doc); 455216154Snwhitehorn _http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL, 456209812Snwhitehorn op, URL->scheme, host, URL->port, URL->doc); 457209812Snwhitehorn 458209812Snwhitehorn /* start sending headers away */ 459209812Snwhitehorn if (URL->user[0] || URL->pwd[0]) { 460209812Snwhitehorn char *auth_str = _http_auth(URL->user, URL->pwd); 46199657Sbenno if (!auth_str) 46299657Sbenno return 999; /* XXX wrong */ 46399657Sbenno _http_cmd(f, "Authorization: Basic %s" ENDL, auth_str); 46499657Sbenno free(auth_str); 46599657Sbenno } 46699657Sbenno if (p = getenv("HTTP_PROXY_AUTH")) { 46799657Sbenno char *auth; 46899657Sbenno 46999657Sbenno /* skip leading "basic:*:", if present */ 470216154Snwhitehorn if (strncmp(p, "basic:*:", 6 + 2) == 0) 471209812Snwhitehorn p += 6 + 2; 472209812Snwhitehorn auth = strchr(p, ':'); 473209812Snwhitehorn if (auth != NULL) { 474209812Snwhitehorn int len = auth - p; 475209812Snwhitehorn char *user; 476209812Snwhitehorn char *auth_str; 477209812Snwhitehorn 478209812Snwhitehorn if ((user = (char*)malloc(len + 1)) == NULL) { 479216154Snwhitehorn free(auth); 480216154Snwhitehorn return 999; /* XXX wrong */ 481209812Snwhitehorn } 482209812Snwhitehorn strncpy(user, p, len); 483209812Snwhitehorn user[len] = 0; 48499657Sbenno auth++; 48599657Sbenno auth_str = _http_auth(user, auth); 486209812Snwhitehorn free(user); 48799657Sbenno if (auth_str == NULL) 48899657Sbenno return 999; /* XXX wrong */ 48999657Sbenno _http_cmd(f, "Proxy-Authorization: Basic %s" ENDL, auth_str); 49099657Sbenno free(auth_str); 49199657Sbenno } else { 49299657Sbenno return 999; /* XXX wrong */ 49399657Sbenno } 494209812Snwhitehorn } 49599657Sbenno _http_cmd(f, "Host: %s:%d" ENDL, host, URL->port); 496239008Sjhb _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname); 497118081Smux if (URL->offset) 498118081Smux _http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset); 499118081Smux _http_cmd(f, "Connection: close" ENDL ENDL); 500118081Smux 501118081Smux /* get response */ 502118081Smux if ((ln = fgetln(f, &len)) == NULL) 503209812Snwhitehorn return 999; 504216154Snwhitehorn DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n", 505209812Snwhitehorn (int)len-2, (int)len-2, ln)); 506118081Smux 507118081Smux /* we can't use strchr() and friends since ln isn't NUL-terminated */ 508239008Sjhb p = ln; 509239008Sjhb while ((p < ln + len) && !isspace(*p)) 510239008Sjhb p++; 511239008Sjhb while ((p < ln + len) && !isdigit(*p)) 512239008Sjhb p++; 513239008Sjhb if (!isdigit(*p)) 514118081Smux return 999; 515170421Smarcel 516170421Smarcel e = atoi(p); 517170421Smarcel DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", e)); 518170421Smarcel return e; 519170421Smarcel} 520170421Smarcel 521209812Snwhitehorn/* 522170421Smarcel * Retrieve a file by HTTP 523209812Snwhitehorn */ 524209812SnwhitehornFILE * 525239008SjhbfetchGetHTTP(struct url *URL, char *flags) 526239008Sjhb{ 527170421Smarcel int e, enc = ENC_NONE, i, noredirect; 528209812Snwhitehorn struct cookie *c; 529209812Snwhitehorn char *ln, *p, *q; 530209812Snwhitehorn FILE *f, *cf; 531209812Snwhitehorn size_t len; 532209812Snwhitehorn off_t pos = 0; 533209812Snwhitehorn 534209812Snwhitehorn noredirect = (flags && strchr(flags, 'A')); 535254025Sjeff 536239008Sjhb /* allocate cookie */ 537239008Sjhb if ((c = calloc(1, sizeof *c)) == NULL) 538239008Sjhb return NULL; 539209812Snwhitehorn 540209812Snwhitehorn /* connect */ 541209812Snwhitehorn if ((f = _http_connect(URL, flags)) == NULL) { 542209812Snwhitehorn free(c); 543209812Snwhitehorn return NULL; 544213282Sneel } 545209812Snwhitehorn c->real_f = f; 546209812Snwhitehorn 547209812Snwhitehorn e = _http_request(f, "GET", URL, flags); 548209812Snwhitehorn if (e != (URL->offset ? HTTP_PARTIAL : HTTP_OK) 549209812Snwhitehorn && (e != HTTP_MOVED || noredirect)) { 55099657Sbenno _http_seterr(e); 55199657Sbenno free(c); 55299657Sbenno fclose(f); 553209812Snwhitehorn return NULL; 55499657Sbenno } 55599657Sbenno 55678342Sbenno /* browse through header */ 55799657Sbenno while (1) { 55878342Sbenno if ((ln = fgetln(f, &len)) == NULL) 559216154Snwhitehorn goto fouch; 560239008Sjhb if ((ln[0] == '\r') || (ln[0] == '\n')) 56199657Sbenno break; 562239008Sjhb while (isspace(ln[len-1])) 563254025Sjeff --len; 564239008Sjhb ln[len] = '\0'; /* XXX */ 565209812Snwhitehorn DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 56699657Sbenno if ((p = _http_match("Location", ln)) != NULL) { 56778342Sbenno struct url *url; 568246713Skib 569246713Skib for (q = p; *q && !isspace(*q); q++) 570246713Skib /* VOID */ ; 571108939Sgrehan *q = 0; 572246713Skib if ((url = fetchParseURL(p)) == NULL) 573108939Sgrehan goto fouch; 574108939Sgrehan url->offset = URL->offset; 575246713Skib url->length = URL->length; 576246713Skib DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 577246713Skib cf = fetchGetHTTP(url, flags); 578246713Skib fetchFreeURL(url); 579246713Skib fclose(f); 580246713Skib return cf; 581246713Skib } else if ((p = _http_match("Transfer-Encoding", ln)) != NULL) { 582246713Skib for (q = p; *q && !isspace(*q); q++) 583246713Skib /* VOID */ ; 584246713Skib *q = 0; 585246713Skib if (strcasecmp(p, "chunked") == 0) 586246713Skib enc = ENC_CHUNKED; 587246713Skib DEBUG(fprintf(stderr, "transfer encoding: [\033[1m%s\033[m]\n", p)); 588246713Skib } else if ((p = _http_match("Content-Type", ln)) != NULL) { 589246713Skib for (i = 0; *p && i < HTTPCTYPELEN; p++, i++) 590246713Skib c->content_type[i] = *p; 591246713Skib do c->content_type[i--] = 0; while (isspace(c->content_type[i])); 592246713Skib DEBUG(fprintf(stderr, "content type: [\033[1m%s\033[m]\n", 593246713Skib c->content_type)); 594246713Skib } else if ((p = _http_match("Content-Range", ln)) != NULL) { 595246713Skib if (strncasecmp(p, "bytes ", 6) != 0) 596246713Skib goto fouch; 597209812Snwhitehorn p += 6; 598246713Skib while (*p && isdigit(*p)) 599246713Skib pos = pos * 10 + (*p++ - '0'); 600246713Skib /* XXX wouldn't hurt to be slightly more paranoid here */ 601246713Skib DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", pos)); 602246713Skib if (pos > URL->offset) 603246713Skib goto fouch; 604246713Skib } 605246713Skib } 606246713Skib 607209812Snwhitehorn /* only body remains */ 608209812Snwhitehorn c->encoding = enc; 609209812Snwhitehorn cf = funopen(c, 610216154Snwhitehorn (int (*)(void *, char *, int))_http_readfn, 611209812Snwhitehorn (int (*)(void *, const char *, int))_http_writefn, 612209812Snwhitehorn (fpos_t (*)(void *, fpos_t, int))NULL, 613209812Snwhitehorn (int (*)(void *))_http_closefn); 614209812Snwhitehorn if (cf == NULL) 615209812Snwhitehorn goto fouch; 616209812Snwhitehorn 617209812Snwhitehorn while (pos < URL->offset) 618209812Snwhitehorn if (fgetc(cf) == EOF) 619209812Snwhitehorn goto cfouch; 620209812Snwhitehorn 621209812Snwhitehorn return cf; 622246713Skib 623246713Skibfouch: 624246713Skib fclose(f); 625209812Snwhitehorn free(c); 626209812Snwhitehorn _http_seterr(999); /* XXX do this properly RSN */ 627209812Snwhitehorn return NULL; 628209812Snwhitehorncfouch: 629209812Snwhitehorn fclose(cf); 630209812Snwhitehorn _http_seterr(999); /* XXX do this properly RSN */ 631209812Snwhitehorn return NULL; 632209812Snwhitehorn} 633209812Snwhitehorn 634246713SkibFILE * 635209812SnwhitehornfetchPutHTTP(struct url *URL, char *flags) 636246713Skib{ 637246713Skib warnx("fetchPutHTTP(): not implemented"); 638246713Skib return NULL; 639246713Skib} 640209812Snwhitehorn 641246713Skib/* 642246713Skib * Get an HTTP document's metadata 643246713Skib */ 644246713Skibint 645246713SkibfetchStatHTTP(struct url *URL, struct url_stat *us, char *flags) 646209812Snwhitehorn{ 647246713Skib int e, noredirect; 648246713Skib size_t len; 649246713Skib char *ln, *p, *q; 650246713Skib FILE *f; 651246713Skib 652246713Skib noredirect = (flags && strchr(flags, 'A')); 653246713Skib 654246713Skib us->size = -1; 655209812Snwhitehorn us->atime = us->mtime = 0; 656246713Skib 657209812Snwhitehorn /* connect */ 658246713Skib if ((f = _http_connect(URL, flags)) == NULL) 659246713Skib return -1; 660108939Sgrehan 661246713Skib e = _http_request(f, "HEAD", URL, flags); 662246713Skib if (e != HTTP_OK && (e != HTTP_MOVED || noredirect)) { 663246713Skib _http_seterr(e); 664246713Skib fclose(f); 665246713Skib return -1; 666246713Skib } 667246713Skib 668246713Skib while (1) { 669246713Skib if ((ln = fgetln(f, &len)) == NULL) 670209812Snwhitehorn goto fouch; 671246713Skib if ((ln[0] == '\r') || (ln[0] == '\n')) 672246713Skib break; 673246713Skib while (isspace(ln[len-1])) 674246713Skib --len; 675246713Skib ln[len] = '\0'; /* XXX */ 676246713Skib DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 677246713Skib if ((p = _http_match("Location", ln)) != NULL) { 678246713Skib struct url *url; 679246713Skib 680108939Sgrehan for (q = p; *q && !isspace(*q); q++) 681246713Skib /* VOID */ ; 682246713Skib *q = 0; 683246713Skib if ((url = fetchParseURL(p)) == NULL) 684246713Skib goto ouch; 685246713Skib url->offset = URL->offset; 686246713Skib url->length = URL->length; 687246713Skib DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 688246713Skib e = fetchStatHTTP(url, us, flags); 689246713Skib fetchFreeURL(url); 690246713Skib fclose(f); 691246713Skib return e; 692246713Skib } else if ((p = _http_match("Last-Modified", ln)) != NULL) { 693246713Skib struct tm tm; 694246713Skib char locale[64]; 695246713Skib 696246713Skib strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale); 697246713Skib setlocale(LC_TIME, "C"); 698246713Skib strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 699108939Sgrehan /* XXX should add support for date-2 and date-3 */ 700108939Sgrehan setlocale(LC_TIME, locale); 701108939Sgrehan us->atime = us->mtime = timegm(&tm); 702108939Sgrehan DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 703108939Sgrehan "%02d:%02d:%02d\033[m]\n", 704246713Skib tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 705108939Sgrehan tm.tm_hour, tm.tm_min, tm.tm_sec)); 706108939Sgrehan } else if ((p = _http_match("Content-Length", ln)) != NULL) { 707108939Sgrehan us->size = 0; 708246713Skib while (*p && isdigit(*p)) 709246713Skib us->size = us->size * 10 + (*p++ - '0'); 710170979Syongari DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", us->size)); 711170979Syongari } 712246713Skib } 713246713Skib 714246713Skib fclose(f); 715246713Skib return 0; 716246713Skib ouch: 717246713Skib _http_seterr(999); /* XXX do this properly RSN */ 718170979Syongari fouch: 719246713Skib fclose(f); 720246713Skib return -1; 721246713Skib} 722170979Syongari 723246713Skib/* 724246713Skib * List a directory 725246713Skib */ 726246713Skibstruct url_ent * 727246713SkibfetchListHTTP(struct url *url, char *flags) 728246713Skib{ 729246713Skib warnx("fetchListHTTP(): not implemented"); 730246713Skib return NULL; 731246713Skib} 732246713Skib