14ca1ab94SDag-Erling Smørgrav /*- 24ca1ab94SDag-Erling Smørgrav * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 34ca1ab94SDag-Erling Smørgrav * All rights reserved. 44ca1ab94SDag-Erling Smørgrav * 54ca1ab94SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 64ca1ab94SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 74ca1ab94SDag-Erling Smørgrav * are met: 84ca1ab94SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 94ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 104ca1ab94SDag-Erling Smørgrav * in this position and unchanged. 114ca1ab94SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 124ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 134ca1ab94SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 144ca1ab94SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 154ca1ab94SDag-Erling Smørgrav * derived from this software without specific prior written permission 164ca1ab94SDag-Erling Smørgrav * 174ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 184ca1ab94SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 194ca1ab94SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 204ca1ab94SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 214ca1ab94SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 224ca1ab94SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 234ca1ab94SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 244ca1ab94SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 254ca1ab94SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 264ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 274ca1ab94SDag-Erling Smørgrav * 287f3dea24SPeter Wemm * $FreeBSD$ 294ca1ab94SDag-Erling Smørgrav */ 304ca1ab94SDag-Erling Smørgrav 31f62e5228SDag-Erling Smørgrav /* 32f62e5228SDag-Erling Smørgrav * The base64 code in this file is based on code from MIT fetch, which 33f62e5228SDag-Erling Smørgrav * has the following copyright and license: 34f62e5228SDag-Erling Smørgrav * 35f62e5228SDag-Erling Smørgrav *- 36f62e5228SDag-Erling Smørgrav * Copyright 1997 Massachusetts Institute of Technology 37f62e5228SDag-Erling Smørgrav * 38f62e5228SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software and 39f62e5228SDag-Erling Smørgrav * its documentation for any purpose and without fee is hereby 40f62e5228SDag-Erling Smørgrav * granted, provided that both the above copyright notice and this 41f62e5228SDag-Erling Smørgrav * permission notice appear in all copies, that both the above 42f62e5228SDag-Erling Smørgrav * copyright notice and this permission notice appear in all 43f62e5228SDag-Erling Smørgrav * supporting documentation, and that the name of M.I.T. not be used 44f62e5228SDag-Erling Smørgrav * in advertising or publicity pertaining to distribution of the 45f62e5228SDag-Erling Smørgrav * software without specific, written prior permission. M.I.T. makes 46f62e5228SDag-Erling Smørgrav * no representations about the suitability of this software for any 47f62e5228SDag-Erling Smørgrav * purpose. It is provided "as is" without express or implied 48f62e5228SDag-Erling Smørgrav * warranty. 49f62e5228SDag-Erling Smørgrav * 50f62e5228SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 51f62e5228SDag-Erling Smørgrav * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 52f62e5228SDag-Erling Smørgrav * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 53f62e5228SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 54f62e5228SDag-Erling Smørgrav * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 55f62e5228SDag-Erling Smørgrav * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 56f62e5228SDag-Erling Smørgrav * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 57f62e5228SDag-Erling Smørgrav * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 58f62e5228SDag-Erling Smørgrav * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 59f62e5228SDag-Erling Smørgrav * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 60f62e5228SDag-Erling Smørgrav * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61f62e5228SDag-Erling Smørgrav * SUCH DAMAGE. */ 62f62e5228SDag-Erling Smørgrav 634ca1ab94SDag-Erling Smørgrav #include <sys/param.h> 6428c645cfSHajimu UMEMOTO #include <sys/socket.h> 654ca1ab94SDag-Erling Smørgrav 664ca1ab94SDag-Erling Smørgrav #include <err.h> 674ca1ab94SDag-Erling Smørgrav #include <ctype.h> 6860245e42SDag-Erling Smørgrav #include <locale.h> 69e6182307SDag-Erling Smørgrav #include <netdb.h> 70f62e5228SDag-Erling Smørgrav #include <stdarg.h> 714ca1ab94SDag-Erling Smørgrav #include <stdio.h> 724ca1ab94SDag-Erling Smørgrav #include <stdlib.h> 734ca1ab94SDag-Erling Smørgrav #include <string.h> 7460245e42SDag-Erling Smørgrav #include <time.h> 754ca1ab94SDag-Erling Smørgrav #include <unistd.h> 764ca1ab94SDag-Erling Smørgrav 774ca1ab94SDag-Erling Smørgrav #include "fetch.h" 78842a95ccSDag-Erling Smørgrav #include "common.h" 790fba3a00SDag-Erling Smørgrav #include "httperr.h" 804ca1ab94SDag-Erling Smørgrav 814ca1ab94SDag-Erling Smørgrav extern char *__progname; 824ca1ab94SDag-Erling Smørgrav 834ca1ab94SDag-Erling Smørgrav #define ENDL "\r\n" 844ca1ab94SDag-Erling Smørgrav 853d2a8471SDag-Erling Smørgrav #define HTTP_OK 200 863d2a8471SDag-Erling Smørgrav #define HTTP_PARTIAL 206 876f9febf9SDag-Erling Smørgrav #define HTTP_MOVED 302 883d2a8471SDag-Erling Smørgrav 894ca1ab94SDag-Erling Smørgrav struct cookie 904ca1ab94SDag-Erling Smørgrav { 914ca1ab94SDag-Erling Smørgrav FILE *real_f; 924ca1ab94SDag-Erling Smørgrav #define ENC_NONE 0 934ca1ab94SDag-Erling Smørgrav #define ENC_CHUNKED 1 944ca1ab94SDag-Erling Smørgrav int encoding; /* 1 = chunked, 0 = none */ 954ca1ab94SDag-Erling Smørgrav #define HTTPCTYPELEN 59 964ca1ab94SDag-Erling Smørgrav char content_type[HTTPCTYPELEN+1]; 974ca1ab94SDag-Erling Smørgrav char *buf; 984ca1ab94SDag-Erling Smørgrav int b_cur, eof; 994ca1ab94SDag-Erling Smørgrav unsigned b_len, chunksize; 1004ca1ab94SDag-Erling Smørgrav }; 1014ca1ab94SDag-Erling Smørgrav 102f62e5228SDag-Erling Smørgrav /* 103f62e5228SDag-Erling Smørgrav * Send a formatted line; optionally echo to terminal 104f62e5228SDag-Erling Smørgrav */ 105f62e5228SDag-Erling Smørgrav static int 106f62e5228SDag-Erling Smørgrav _http_cmd(FILE *f, char *fmt, ...) 107f62e5228SDag-Erling Smørgrav { 108f62e5228SDag-Erling Smørgrav va_list ap; 109f62e5228SDag-Erling Smørgrav 110f62e5228SDag-Erling Smørgrav va_start(ap, fmt); 111f62e5228SDag-Erling Smørgrav vfprintf(f, fmt, ap); 11235f723dbSDag-Erling Smørgrav DEBUG(fprintf(stderr, "\033[1m>>> ")); 11335f723dbSDag-Erling Smørgrav DEBUG(vfprintf(stderr, fmt, ap)); 11435f723dbSDag-Erling Smørgrav DEBUG(fprintf(stderr, "\033[m")); 115f62e5228SDag-Erling Smørgrav va_end(ap); 116f62e5228SDag-Erling Smørgrav 117f62e5228SDag-Erling Smørgrav return 0; /* XXX */ 118f62e5228SDag-Erling Smørgrav } 119f62e5228SDag-Erling Smørgrav 120f62e5228SDag-Erling Smørgrav /* 121f62e5228SDag-Erling Smørgrav * Fill the input buffer, do chunk decoding on the fly 122f62e5228SDag-Erling Smørgrav */ 1234ca1ab94SDag-Erling Smørgrav static char * 1244ca1ab94SDag-Erling Smørgrav _http_fillbuf(struct cookie *c) 1254ca1ab94SDag-Erling Smørgrav { 1264ca1ab94SDag-Erling Smørgrav char *ln; 1274ca1ab94SDag-Erling Smørgrav unsigned int len; 1284ca1ab94SDag-Erling Smørgrav 1294ca1ab94SDag-Erling Smørgrav if (c->eof) 1304ca1ab94SDag-Erling Smørgrav return NULL; 1314ca1ab94SDag-Erling Smørgrav 1324ca1ab94SDag-Erling Smørgrav if (c->encoding == ENC_NONE) { 1334ca1ab94SDag-Erling Smørgrav c->buf = fgetln(c->real_f, &(c->b_len)); 1344ca1ab94SDag-Erling Smørgrav c->b_cur = 0; 1354ca1ab94SDag-Erling Smørgrav } else if (c->encoding == ENC_CHUNKED) { 1364ca1ab94SDag-Erling Smørgrav if (c->chunksize == 0) { 1374ca1ab94SDag-Erling Smørgrav ln = fgetln(c->real_f, &len); 1386efb30c8SDag-Erling Smørgrav if (len <= 2) 1396efb30c8SDag-Erling Smørgrav return NULL; 1404ca1ab94SDag-Erling Smørgrav DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: " 1414ca1ab94SDag-Erling Smørgrav "%*.*s\033[m\n", (int)len-2, (int)len-2, ln)); 1424ca1ab94SDag-Erling Smørgrav sscanf(ln, "%x", &(c->chunksize)); 1434ca1ab94SDag-Erling Smørgrav if (!c->chunksize) { 1444ca1ab94SDag-Erling Smørgrav DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 1454ca1ab94SDag-Erling Smørgrav "end of last chunk\033[m\n")); 1464ca1ab94SDag-Erling Smørgrav c->eof = 1; 1474ca1ab94SDag-Erling Smørgrav return NULL; 1484ca1ab94SDag-Erling Smørgrav } 1494ca1ab94SDag-Erling Smørgrav DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 1504ca1ab94SDag-Erling Smørgrav "new chunk: %X\033[m\n", c->chunksize)); 1514ca1ab94SDag-Erling Smørgrav } 1524ca1ab94SDag-Erling Smørgrav c->buf = fgetln(c->real_f, &(c->b_len)); 1534ca1ab94SDag-Erling Smørgrav if (c->b_len > c->chunksize) 1544ca1ab94SDag-Erling Smørgrav c->b_len = c->chunksize; 1554ca1ab94SDag-Erling Smørgrav c->chunksize -= c->b_len; 1564ca1ab94SDag-Erling Smørgrav c->b_cur = 0; 1574ca1ab94SDag-Erling Smørgrav } 1584ca1ab94SDag-Erling Smørgrav else return NULL; /* unknown encoding */ 1594ca1ab94SDag-Erling Smørgrav return c->buf; 1604ca1ab94SDag-Erling Smørgrav } 1614ca1ab94SDag-Erling Smørgrav 162f62e5228SDag-Erling Smørgrav /* 163f62e5228SDag-Erling Smørgrav * Read function 164f62e5228SDag-Erling Smørgrav */ 1654ca1ab94SDag-Erling Smørgrav static int 1664ca1ab94SDag-Erling Smørgrav _http_readfn(struct cookie *c, char *buf, int len) 1674ca1ab94SDag-Erling Smørgrav { 1684ca1ab94SDag-Erling Smørgrav int l, pos = 0; 1694ca1ab94SDag-Erling Smørgrav while (len) { 1704ca1ab94SDag-Erling Smørgrav /* empty buffer */ 1714ca1ab94SDag-Erling Smørgrav if (!c->buf || (c->b_cur == c->b_len)) 1724ca1ab94SDag-Erling Smørgrav if (!_http_fillbuf(c)) 1734ca1ab94SDag-Erling Smørgrav break; 1744ca1ab94SDag-Erling Smørgrav 1754ca1ab94SDag-Erling Smørgrav l = c->b_len - c->b_cur; 1764ca1ab94SDag-Erling Smørgrav if (len < l) l = len; 1774ca1ab94SDag-Erling Smørgrav memcpy(buf + pos, c->buf + c->b_cur, l); 1784ca1ab94SDag-Erling Smørgrav c->b_cur += l; 1794ca1ab94SDag-Erling Smørgrav pos += l; 1804ca1ab94SDag-Erling Smørgrav len -= l; 1814ca1ab94SDag-Erling Smørgrav } 1824ca1ab94SDag-Erling Smørgrav 1834ca1ab94SDag-Erling Smørgrav if (ferror(c->real_f)) 1844ca1ab94SDag-Erling Smørgrav return -1; 1854ca1ab94SDag-Erling Smørgrav else return pos; 1864ca1ab94SDag-Erling Smørgrav } 1874ca1ab94SDag-Erling Smørgrav 188f62e5228SDag-Erling Smørgrav /* 189f62e5228SDag-Erling Smørgrav * Write function 190f62e5228SDag-Erling Smørgrav */ 1914ca1ab94SDag-Erling Smørgrav static int 1924ca1ab94SDag-Erling Smørgrav _http_writefn(struct cookie *c, const char *buf, int len) 1934ca1ab94SDag-Erling Smørgrav { 1944ca1ab94SDag-Erling Smørgrav size_t r = fwrite(buf, 1, (size_t)len, c->real_f); 1954ca1ab94SDag-Erling Smørgrav return r ? r : -1; 1964ca1ab94SDag-Erling Smørgrav } 1974ca1ab94SDag-Erling Smørgrav 198f62e5228SDag-Erling Smørgrav /* 199f62e5228SDag-Erling Smørgrav * Close function 200f62e5228SDag-Erling Smørgrav */ 2014ca1ab94SDag-Erling Smørgrav static int 2024ca1ab94SDag-Erling Smørgrav _http_closefn(struct cookie *c) 2034ca1ab94SDag-Erling Smørgrav { 2044ca1ab94SDag-Erling Smørgrav int r = fclose(c->real_f); 2054ca1ab94SDag-Erling Smørgrav free(c); 2064ca1ab94SDag-Erling Smørgrav return (r == EOF) ? -1 : 0; 2074ca1ab94SDag-Erling Smørgrav } 2084ca1ab94SDag-Erling Smørgrav 209f62e5228SDag-Erling Smørgrav /* 210f62e5228SDag-Erling Smørgrav * Extract content type from cookie 211f62e5228SDag-Erling Smørgrav */ 2124ca1ab94SDag-Erling Smørgrav char * 2134ca1ab94SDag-Erling Smørgrav fetchContentType(FILE *f) 2144ca1ab94SDag-Erling Smørgrav { 2154ca1ab94SDag-Erling Smørgrav /* 2164ca1ab94SDag-Erling Smørgrav * We have no way of making sure this really *is* one of our cookies, 2174ca1ab94SDag-Erling Smørgrav * so just check for a null pointer and hope for the best. 2184ca1ab94SDag-Erling Smørgrav */ 2194ca1ab94SDag-Erling Smørgrav return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL; 2204ca1ab94SDag-Erling Smørgrav } 2214ca1ab94SDag-Erling Smørgrav 222f62e5228SDag-Erling Smørgrav /* 223f62e5228SDag-Erling Smørgrav * Base64 encoding 224f62e5228SDag-Erling Smørgrav */ 22535f723dbSDag-Erling Smørgrav static char * 22635f723dbSDag-Erling Smørgrav _http_base64(char *src) 227f62e5228SDag-Erling Smørgrav { 228f62e5228SDag-Erling Smørgrav static const char base64[] = 229f62e5228SDag-Erling Smørgrav "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 230f62e5228SDag-Erling Smørgrav "abcdefghijklmnopqrstuvwxyz" 231f62e5228SDag-Erling Smørgrav "0123456789+/"; 23235f723dbSDag-Erling Smørgrav char *str, *dst; 23335f723dbSDag-Erling Smørgrav size_t l; 23435f723dbSDag-Erling Smørgrav int t, r; 23535f723dbSDag-Erling Smørgrav 23635f723dbSDag-Erling Smørgrav l = strlen(src); 23735f723dbSDag-Erling Smørgrav if ((str = malloc(((l + 2) / 3) * 4)) == NULL) 23835f723dbSDag-Erling Smørgrav return NULL; 23935f723dbSDag-Erling Smørgrav dst = str; 24035f723dbSDag-Erling Smørgrav r = 0; 241f62e5228SDag-Erling Smørgrav 242f62e5228SDag-Erling Smørgrav while (l >= 3) { 243f62e5228SDag-Erling Smørgrav t = (src[0] << 16) | (src[1] << 8) | src[2]; 244f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 245f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 246f62e5228SDag-Erling Smørgrav dst[2] = base64[(t >> 6) & 0x3f]; 247f62e5228SDag-Erling Smørgrav dst[3] = base64[(t >> 0) & 0x3f]; 248f62e5228SDag-Erling Smørgrav src += 3; l -= 3; 249f62e5228SDag-Erling Smørgrav dst += 4; r += 4; 250f62e5228SDag-Erling Smørgrav } 251f62e5228SDag-Erling Smørgrav 252f62e5228SDag-Erling Smørgrav switch (l) { 253f62e5228SDag-Erling Smørgrav case 2: 254f62e5228SDag-Erling Smørgrav t = (src[0] << 16) | (src[1] << 8); 255f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 256f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 257f62e5228SDag-Erling Smørgrav dst[2] = base64[(t >> 6) & 0x3f]; 258f62e5228SDag-Erling Smørgrav dst[3] = '='; 259f62e5228SDag-Erling Smørgrav dst += 4; 260f62e5228SDag-Erling Smørgrav r += 4; 261f62e5228SDag-Erling Smørgrav break; 262f62e5228SDag-Erling Smørgrav case 1: 263f62e5228SDag-Erling Smørgrav t = src[0] << 16; 264f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 265f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 266f62e5228SDag-Erling Smørgrav dst[2] = dst[3] = '='; 267f62e5228SDag-Erling Smørgrav dst += 4; 268f62e5228SDag-Erling Smørgrav r += 4; 269f62e5228SDag-Erling Smørgrav break; 270f62e5228SDag-Erling Smørgrav case 0: 271f62e5228SDag-Erling Smørgrav break; 272f62e5228SDag-Erling Smørgrav } 273f62e5228SDag-Erling Smørgrav 274f62e5228SDag-Erling Smørgrav *dst = 0; 27535f723dbSDag-Erling Smørgrav return str; 276f62e5228SDag-Erling Smørgrav } 277f62e5228SDag-Erling Smørgrav 278f62e5228SDag-Erling Smørgrav /* 279f62e5228SDag-Erling Smørgrav * Encode username and password 280f62e5228SDag-Erling Smørgrav */ 28135f723dbSDag-Erling Smørgrav static int 28235f723dbSDag-Erling Smørgrav _http_basic_auth(FILE *f, char *hdr, char *usr, char *pwd) 283f62e5228SDag-Erling Smørgrav { 28435f723dbSDag-Erling Smørgrav char *upw, *auth; 28535f723dbSDag-Erling Smørgrav int r; 286f62e5228SDag-Erling Smørgrav 28735f723dbSDag-Erling Smørgrav if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 28835f723dbSDag-Erling Smørgrav return -1; 28935f723dbSDag-Erling Smørgrav auth = _http_base64(upw); 29035f723dbSDag-Erling Smørgrav free(upw); 29135f723dbSDag-Erling Smørgrav if (auth == NULL) 29235f723dbSDag-Erling Smørgrav return -1; 29335f723dbSDag-Erling Smørgrav r = _http_cmd(f, "%s: Basic %s" ENDL, hdr, auth); 29435f723dbSDag-Erling Smørgrav free(auth); 29535f723dbSDag-Erling Smørgrav return r; 2964d029f13SDag-Erling Smørgrav } 29735f723dbSDag-Erling Smørgrav 29835f723dbSDag-Erling Smørgrav /* 29935f723dbSDag-Erling Smørgrav * Send an authorization header 30035f723dbSDag-Erling Smørgrav */ 30135f723dbSDag-Erling Smørgrav static int 30235f723dbSDag-Erling Smørgrav _http_authorize(FILE *f, char *hdr, char *p) 30335f723dbSDag-Erling Smørgrav { 30435f723dbSDag-Erling Smørgrav /* basic authorization */ 30535f723dbSDag-Erling Smørgrav if (strncasecmp(p, "basic:", 6) == 0) { 30635f723dbSDag-Erling Smørgrav char *user, *pwd, *str; 30735f723dbSDag-Erling Smørgrav int r; 30835f723dbSDag-Erling Smørgrav 30935f723dbSDag-Erling Smørgrav /* skip realm */ 31035f723dbSDag-Erling Smørgrav for (p += 6; *p && *p != ':'; ++p) 31135f723dbSDag-Erling Smørgrav /* nothing */ ; 31235f723dbSDag-Erling Smørgrav if (!*p || strchr(++p, ':') == NULL) 31335f723dbSDag-Erling Smørgrav return -1; 31435f723dbSDag-Erling Smørgrav if ((str = strdup(p)) == NULL) 31535f723dbSDag-Erling Smørgrav return -1; /* XXX */ 31635f723dbSDag-Erling Smørgrav user = str; 31735f723dbSDag-Erling Smørgrav pwd = strchr(str, ':'); 31835f723dbSDag-Erling Smørgrav *pwd++ = '\0'; 31935f723dbSDag-Erling Smørgrav r = _http_basic_auth(f, hdr, user, pwd); 32035f723dbSDag-Erling Smørgrav free(str); 32135f723dbSDag-Erling Smørgrav return r; 3224d029f13SDag-Erling Smørgrav } 32335f723dbSDag-Erling Smørgrav return -1; 324f62e5228SDag-Erling Smørgrav } 325f62e5228SDag-Erling Smørgrav 326f62e5228SDag-Erling Smørgrav /* 32760245e42SDag-Erling Smørgrav * Connect to server or proxy 328f62e5228SDag-Erling Smørgrav */ 32935f723dbSDag-Erling Smørgrav static FILE * 33035f723dbSDag-Erling Smørgrav _http_connect(struct url *URL, char *flags, int *proxy) 3314ca1ab94SDag-Erling Smørgrav { 33260245e42SDag-Erling Smørgrav int direct, sd = -1, verbose; 33328c645cfSHajimu UMEMOTO #ifdef INET6 33428c645cfSHajimu UMEMOTO int af = AF_UNSPEC; 33528c645cfSHajimu UMEMOTO #else 33628c645cfSHajimu UMEMOTO int af = AF_INET; 33728c645cfSHajimu UMEMOTO #endif 3384ca1ab94SDag-Erling Smørgrav size_t len; 33960245e42SDag-Erling Smørgrav char *px; 34060245e42SDag-Erling Smørgrav FILE *f; 3414ca1ab94SDag-Erling Smørgrav 342f5f109a0SDag-Erling Smørgrav direct = (flags && strchr(flags, 'd')); 343f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 34428c645cfSHajimu UMEMOTO if ((flags && strchr(flags, '4'))) 34528c645cfSHajimu UMEMOTO af = AF_INET; 34628c645cfSHajimu UMEMOTO else if ((flags && strchr(flags, '6'))) 34728c645cfSHajimu UMEMOTO af = AF_INET6; 3480fba3a00SDag-Erling Smørgrav 3494ca1ab94SDag-Erling Smørgrav /* check port */ 350e6182307SDag-Erling Smørgrav if (!URL->port) { 351e6182307SDag-Erling Smørgrav struct servent *se; 352e6182307SDag-Erling Smørgrav 353c97925adSHajimu UMEMOTO if (strcasecmp(URL->scheme, "ftp") == 0) 354c97925adSHajimu UMEMOTO if ((se = getservbyname("ftp", "tcp")) != NULL) 355c97925adSHajimu UMEMOTO URL->port = ntohs(se->s_port); 356c97925adSHajimu UMEMOTO else 357c97925adSHajimu UMEMOTO URL->port = 21; 358c97925adSHajimu UMEMOTO else 359e6182307SDag-Erling Smørgrav if ((se = getservbyname("http", "tcp")) != NULL) 360e6182307SDag-Erling Smørgrav URL->port = ntohs(se->s_port); 361e6182307SDag-Erling Smørgrav else 362e6182307SDag-Erling Smørgrav URL->port = 80; 363e6182307SDag-Erling Smørgrav } 3644ca1ab94SDag-Erling Smørgrav 3654ca1ab94SDag-Erling Smørgrav /* attempt to connect to proxy server */ 366f5f109a0SDag-Erling Smørgrav if (!direct && (px = getenv("HTTP_PROXY")) != NULL) { 3673b7a6740SDag-Erling Smørgrav char host[MAXHOSTNAMELEN]; 368e6182307SDag-Erling Smørgrav int port = 0; 3694ca1ab94SDag-Erling Smørgrav 3704ca1ab94SDag-Erling Smørgrav /* measure length */ 37128c645cfSHajimu UMEMOTO #ifdef INET6 37228c645cfSHajimu UMEMOTO if (px[0] != '[' || 37328c645cfSHajimu UMEMOTO (len = strcspn(px, "]")) >= strlen(px) || 37428c645cfSHajimu UMEMOTO (px[++len] != '\0' && px[len] != ':')) 37528c645cfSHajimu UMEMOTO #endif 3764ca1ab94SDag-Erling Smørgrav len = strcspn(px, ":"); 3774ca1ab94SDag-Erling Smørgrav 378f5f109a0SDag-Erling Smørgrav /* get port (XXX atoi is a little too tolerant perhaps?) */ 379e6182307SDag-Erling Smørgrav if (px[len] == ':') { 380e6182307SDag-Erling Smørgrav if (strspn(px+len+1, "0123456789") != strlen(px+len+1) 381e6182307SDag-Erling Smørgrav || strlen(px+len+1) > 5) { 382e6182307SDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 383e6182307SDag-Erling Smørgrav } 3844ca1ab94SDag-Erling Smørgrav port = atoi(px+len+1); 385e6182307SDag-Erling Smørgrav if (port < 1 || port > 65535) { 386e6182307SDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 387e6182307SDag-Erling Smørgrav } 388e6182307SDag-Erling Smørgrav } 389e6182307SDag-Erling Smørgrav if (!port) { 390e6182307SDag-Erling Smørgrav #if 0 391e6182307SDag-Erling Smørgrav /* 392e6182307SDag-Erling Smørgrav * commented out, since there is currently no service name 393e6182307SDag-Erling Smørgrav * for HTTP proxies 394e6182307SDag-Erling Smørgrav */ 395e6182307SDag-Erling Smørgrav struct servent *se; 396e6182307SDag-Erling Smørgrav 397e6182307SDag-Erling Smørgrav if ((se = getservbyname("xxxx", "tcp")) != NULL) 398e6182307SDag-Erling Smørgrav port = ntohs(se->s_port); 399e6182307SDag-Erling Smørgrav else 400e6182307SDag-Erling Smørgrav #endif 401e6182307SDag-Erling Smørgrav port = 3128; 402e6182307SDag-Erling Smørgrav } 4034ca1ab94SDag-Erling Smørgrav 4044ca1ab94SDag-Erling Smørgrav /* get host name */ 40528c645cfSHajimu UMEMOTO #ifdef INET6 40628c645cfSHajimu UMEMOTO if (len > 1 && px[0] == '[' && px[len - 1] == ']') { 40728c645cfSHajimu UMEMOTO px++; 40828c645cfSHajimu UMEMOTO len -= 2; 40928c645cfSHajimu UMEMOTO } 41028c645cfSHajimu UMEMOTO #endif 4114ca1ab94SDag-Erling Smørgrav if (len >= MAXHOSTNAMELEN) 4124ca1ab94SDag-Erling Smørgrav len = MAXHOSTNAMELEN - 1; 4134ca1ab94SDag-Erling Smørgrav strncpy(host, px, len); 4144ca1ab94SDag-Erling Smørgrav host[len] = 0; 4154ca1ab94SDag-Erling Smørgrav 4164ca1ab94SDag-Erling Smørgrav /* connect */ 41728c645cfSHajimu UMEMOTO sd = _fetch_connect(host, port, af, verbose); 4184ca1ab94SDag-Erling Smørgrav } 4194ca1ab94SDag-Erling Smørgrav 4204ca1ab94SDag-Erling Smørgrav /* if no proxy is configured or could be contacted, try direct */ 42135f723dbSDag-Erling Smørgrav *proxy = (sd != -1); 42235f723dbSDag-Erling Smørgrav if (!*proxy) { 423c97925adSHajimu UMEMOTO if (strcasecmp(URL->scheme, "ftp") == 0) 424c97925adSHajimu UMEMOTO goto ouch; 42528c645cfSHajimu UMEMOTO if ((sd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1) 4264ca1ab94SDag-Erling Smørgrav goto ouch; 4274ca1ab94SDag-Erling Smørgrav } 4284ca1ab94SDag-Erling Smørgrav 4294ca1ab94SDag-Erling Smørgrav /* reopen as stream */ 4308e3986eaSDag-Erling Smørgrav if ((f = fdopen(sd, "r+")) == NULL) 4314ca1ab94SDag-Erling Smørgrav goto ouch; 43260245e42SDag-Erling Smørgrav 43360245e42SDag-Erling Smørgrav return f; 43460245e42SDag-Erling Smørgrav 43560245e42SDag-Erling Smørgrav ouch: 43660245e42SDag-Erling Smørgrav if (sd >= 0) 43760245e42SDag-Erling Smørgrav close(sd); 43860245e42SDag-Erling Smørgrav _http_seterr(999); /* XXX do this properly RSN */ 43960245e42SDag-Erling Smørgrav return NULL; 44060245e42SDag-Erling Smørgrav } 44160245e42SDag-Erling Smørgrav 44260245e42SDag-Erling Smørgrav /* 4436f9febf9SDag-Erling Smørgrav * Check a header line 4446f9febf9SDag-Erling Smørgrav */ 44535f723dbSDag-Erling Smørgrav static char * 4466f9febf9SDag-Erling Smørgrav _http_match(char *str, char *hdr) 4476f9febf9SDag-Erling Smørgrav { 4486f9febf9SDag-Erling Smørgrav while (*str && *hdr && tolower(*str++) == tolower(*hdr++)) 4496f9febf9SDag-Erling Smørgrav /* nothing */; 4506f9febf9SDag-Erling Smørgrav if (*str || *hdr != ':') 4516f9febf9SDag-Erling Smørgrav return NULL; 4526f9febf9SDag-Erling Smørgrav while (*hdr && isspace(*++hdr)) 4536f9febf9SDag-Erling Smørgrav /* nothing */; 4546f9febf9SDag-Erling Smørgrav return hdr; 4556f9febf9SDag-Erling Smørgrav } 4566f9febf9SDag-Erling Smørgrav 4576f9febf9SDag-Erling Smørgrav /* 45860245e42SDag-Erling Smørgrav * Send a HEAD or GET request 45960245e42SDag-Erling Smørgrav */ 46035f723dbSDag-Erling Smørgrav static int 46135f723dbSDag-Erling Smørgrav _http_request(FILE *f, char *op, struct url *URL, char *flags, int proxy) 46260245e42SDag-Erling Smørgrav { 46360245e42SDag-Erling Smørgrav int e, verbose; 46460245e42SDag-Erling Smørgrav char *ln, *p; 46560245e42SDag-Erling Smørgrav size_t len; 46628c645cfSHajimu UMEMOTO char *host; 46728c645cfSHajimu UMEMOTO #ifdef INET6 46828c645cfSHajimu UMEMOTO char hbuf[MAXHOSTNAMELEN + 1]; 46928c645cfSHajimu UMEMOTO #endif 47060245e42SDag-Erling Smørgrav 47160245e42SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 4724ca1ab94SDag-Erling Smørgrav 47328c645cfSHajimu UMEMOTO host = URL->host; 47428c645cfSHajimu UMEMOTO #ifdef INET6 47528c645cfSHajimu UMEMOTO if (strchr(URL->host, ':')) { 47628c645cfSHajimu UMEMOTO snprintf(hbuf, sizeof(hbuf), "[%s]", URL->host); 47728c645cfSHajimu UMEMOTO host = hbuf; 47828c645cfSHajimu UMEMOTO } 47928c645cfSHajimu UMEMOTO #endif 48028c645cfSHajimu UMEMOTO 48135f723dbSDag-Erling Smørgrav /* send request (proxies require absolute form) */ 4820fba3a00SDag-Erling Smørgrav if (verbose) 483c97925adSHajimu UMEMOTO _fetch_info("requesting %s://%s:%d%s", 48428c645cfSHajimu UMEMOTO URL->scheme, host, URL->port, URL->doc); 48535f723dbSDag-Erling Smørgrav if (proxy) 48660245e42SDag-Erling Smørgrav _http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL, 48728c645cfSHajimu UMEMOTO op, URL->scheme, host, URL->port, URL->doc); 48835f723dbSDag-Erling Smørgrav else 48935f723dbSDag-Erling Smørgrav _http_cmd(f, "%s %s HTTP/1.1" ENDL, op, URL->doc); 4904ca1ab94SDag-Erling Smørgrav 4914ca1ab94SDag-Erling Smørgrav /* start sending headers away */ 49235f723dbSDag-Erling Smørgrav if (URL->user[0] || URL->pwd[0]) 49335f723dbSDag-Erling Smørgrav _http_basic_auth(f, "Authorization", 49435f723dbSDag-Erling Smørgrav URL->user ? URL->user : "", 49535f723dbSDag-Erling Smørgrav URL->pwd ? URL->pwd : ""); 49635f723dbSDag-Erling Smørgrav else if ((p = getenv("HTTP_AUTH")) != NULL) 49735f723dbSDag-Erling Smørgrav _http_authorize(f, "Authorization", p); 49835f723dbSDag-Erling Smørgrav if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL) 49935f723dbSDag-Erling Smørgrav _http_authorize(f, "Proxy-Authorization", p); 50028c645cfSHajimu UMEMOTO _http_cmd(f, "Host: %s:%d" ENDL, host, URL->port); 501f62e5228SDag-Erling Smørgrav _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname); 5023d2a8471SDag-Erling Smørgrav if (URL->offset) 5033d2a8471SDag-Erling Smørgrav _http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset); 504f62e5228SDag-Erling Smørgrav _http_cmd(f, "Connection: close" ENDL ENDL); 5054ca1ab94SDag-Erling Smørgrav 5064ca1ab94SDag-Erling Smørgrav /* get response */ 5074ca1ab94SDag-Erling Smørgrav if ((ln = fgetln(f, &len)) == NULL) 50860245e42SDag-Erling Smørgrav return 999; 5094ca1ab94SDag-Erling Smørgrav DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n", 5104ca1ab94SDag-Erling Smørgrav (int)len-2, (int)len-2, ln)); 5114ca1ab94SDag-Erling Smørgrav 5124ca1ab94SDag-Erling Smørgrav /* we can't use strchr() and friends since ln isn't NUL-terminated */ 5134ca1ab94SDag-Erling Smørgrav p = ln; 5144ca1ab94SDag-Erling Smørgrav while ((p < ln + len) && !isspace(*p)) 5154ca1ab94SDag-Erling Smørgrav p++; 5164ca1ab94SDag-Erling Smørgrav while ((p < ln + len) && !isdigit(*p)) 5174ca1ab94SDag-Erling Smørgrav p++; 5184ca1ab94SDag-Erling Smørgrav if (!isdigit(*p)) 51960245e42SDag-Erling Smørgrav return 999; 52060245e42SDag-Erling Smørgrav 5213b7a6740SDag-Erling Smørgrav e = atoi(p); 5223b7a6740SDag-Erling Smørgrav DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", e)); 52360245e42SDag-Erling Smørgrav return e; 52460245e42SDag-Erling Smørgrav } 52560245e42SDag-Erling Smørgrav 52660245e42SDag-Erling Smørgrav /* 52760245e42SDag-Erling Smørgrav * Retrieve a file by HTTP 52860245e42SDag-Erling Smørgrav */ 52960245e42SDag-Erling Smørgrav FILE * 53060245e42SDag-Erling Smørgrav fetchGetHTTP(struct url *URL, char *flags) 53160245e42SDag-Erling Smørgrav { 53235f723dbSDag-Erling Smørgrav int e, enc = ENC_NONE, i, noredirect, proxy; 53360245e42SDag-Erling Smørgrav struct cookie *c; 53460245e42SDag-Erling Smørgrav char *ln, *p, *q; 53560245e42SDag-Erling Smørgrav FILE *f, *cf; 53660245e42SDag-Erling Smørgrav size_t len; 53760245e42SDag-Erling Smørgrav off_t pos = 0; 53860245e42SDag-Erling Smørgrav 5396f9febf9SDag-Erling Smørgrav noredirect = (flags && strchr(flags, 'A')); 5406f9febf9SDag-Erling Smørgrav 54160245e42SDag-Erling Smørgrav /* allocate cookie */ 54260245e42SDag-Erling Smørgrav if ((c = calloc(1, sizeof *c)) == NULL) 54360245e42SDag-Erling Smørgrav return NULL; 54460245e42SDag-Erling Smørgrav 54560245e42SDag-Erling Smørgrav /* connect */ 54635f723dbSDag-Erling Smørgrav if ((f = _http_connect(URL, flags, &proxy)) == NULL) { 54760245e42SDag-Erling Smørgrav free(c); 54860245e42SDag-Erling Smørgrav return NULL; 54960245e42SDag-Erling Smørgrav } 55060245e42SDag-Erling Smørgrav c->real_f = f; 55160245e42SDag-Erling Smørgrav 55235f723dbSDag-Erling Smørgrav e = _http_request(f, "GET", URL, flags, proxy); 5536f9febf9SDag-Erling Smørgrav if (e != (URL->offset ? HTTP_PARTIAL : HTTP_OK) 5546f9febf9SDag-Erling Smørgrav && (e != HTTP_MOVED || noredirect)) { 5553b7a6740SDag-Erling Smørgrav _http_seterr(e); 556cf5af79cSDag-Erling Smørgrav free(c); 557cf5af79cSDag-Erling Smørgrav fclose(f); 558cf5af79cSDag-Erling Smørgrav return NULL; 5598e3986eaSDag-Erling Smørgrav } 5604ca1ab94SDag-Erling Smørgrav 5614ca1ab94SDag-Erling Smørgrav /* browse through header */ 5624ca1ab94SDag-Erling Smørgrav while (1) { 5634ca1ab94SDag-Erling Smørgrav if ((ln = fgetln(f, &len)) == NULL) 5644ca1ab94SDag-Erling Smørgrav goto fouch; 5654ca1ab94SDag-Erling Smørgrav if ((ln[0] == '\r') || (ln[0] == '\n')) 5664ca1ab94SDag-Erling Smørgrav break; 56760245e42SDag-Erling Smørgrav while (isspace(ln[len-1])) 56860245e42SDag-Erling Smørgrav --len; 56960245e42SDag-Erling Smørgrav ln[len] = '\0'; /* XXX */ 57060245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 5716f9febf9SDag-Erling Smørgrav if ((p = _http_match("Location", ln)) != NULL) { 5726f9febf9SDag-Erling Smørgrav struct url *url; 5736f9febf9SDag-Erling Smørgrav 5746f9febf9SDag-Erling Smørgrav for (q = p; *q && !isspace(*q); q++) 5756f9febf9SDag-Erling Smørgrav /* VOID */ ; 5766f9febf9SDag-Erling Smørgrav *q = 0; 5776f9febf9SDag-Erling Smørgrav if ((url = fetchParseURL(p)) == NULL) 5786f9febf9SDag-Erling Smørgrav goto fouch; 5796f9febf9SDag-Erling Smørgrav url->offset = URL->offset; 5806f9febf9SDag-Erling Smørgrav url->length = URL->length; 5816f9febf9SDag-Erling Smørgrav DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 5826f9febf9SDag-Erling Smørgrav cf = fetchGetHTTP(url, flags); 5836f9febf9SDag-Erling Smørgrav fetchFreeURL(url); 5846f9febf9SDag-Erling Smørgrav fclose(f); 5856f9febf9SDag-Erling Smørgrav return cf; 5866f9febf9SDag-Erling Smørgrav } else if ((p = _http_match("Transfer-Encoding", ln)) != NULL) { 58760245e42SDag-Erling Smørgrav for (q = p; *q && !isspace(*q); q++) 5884ca1ab94SDag-Erling Smørgrav /* VOID */ ; 5894ca1ab94SDag-Erling Smørgrav *q = 0; 5904ca1ab94SDag-Erling Smørgrav if (strcasecmp(p, "chunked") == 0) 5914ca1ab94SDag-Erling Smørgrav enc = ENC_CHUNKED; 59260245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "transfer encoding: [\033[1m%s\033[m]\n", p)); 59360245e42SDag-Erling Smørgrav } else if ((p = _http_match("Content-Type", ln)) != NULL) { 59460245e42SDag-Erling Smørgrav for (i = 0; *p && i < HTTPCTYPELEN; p++, i++) 59560245e42SDag-Erling Smørgrav c->content_type[i] = *p; 5964ca1ab94SDag-Erling Smørgrav do c->content_type[i--] = 0; while (isspace(c->content_type[i])); 59760245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "content type: [\033[1m%s\033[m]\n", 5984ca1ab94SDag-Erling Smørgrav c->content_type)); 59960245e42SDag-Erling Smørgrav } else if ((p = _http_match("Content-Range", ln)) != NULL) { 60060245e42SDag-Erling Smørgrav if (strncasecmp(p, "bytes ", 6) != 0) 6013d2a8471SDag-Erling Smørgrav goto fouch; 60260245e42SDag-Erling Smørgrav p += 6; 60360245e42SDag-Erling Smørgrav while (*p && isdigit(*p)) 6043d2a8471SDag-Erling Smørgrav pos = pos * 10 + (*p++ - '0'); 6053d2a8471SDag-Erling Smørgrav /* XXX wouldn't hurt to be slightly more paranoid here */ 60660245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", pos)); 6073d2a8471SDag-Erling Smørgrav if (pos > URL->offset) 6083d2a8471SDag-Erling Smørgrav goto fouch; 6094ca1ab94SDag-Erling Smørgrav } 6104ca1ab94SDag-Erling Smørgrav } 6114ca1ab94SDag-Erling Smørgrav 6124ca1ab94SDag-Erling Smørgrav /* only body remains */ 6134ca1ab94SDag-Erling Smørgrav c->encoding = enc; 6144ca1ab94SDag-Erling Smørgrav cf = funopen(c, 6154ca1ab94SDag-Erling Smørgrav (int (*)(void *, char *, int))_http_readfn, 6164ca1ab94SDag-Erling Smørgrav (int (*)(void *, const char *, int))_http_writefn, 6174ca1ab94SDag-Erling Smørgrav (fpos_t (*)(void *, fpos_t, int))NULL, 6184ca1ab94SDag-Erling Smørgrav (int (*)(void *))_http_closefn); 6194ca1ab94SDag-Erling Smørgrav if (cf == NULL) 6204ca1ab94SDag-Erling Smørgrav goto fouch; 621e6182307SDag-Erling Smørgrav 6223d2a8471SDag-Erling Smørgrav while (pos < URL->offset) 6233d2a8471SDag-Erling Smørgrav if (fgetc(cf) == EOF) 6243d2a8471SDag-Erling Smørgrav goto cfouch; 6253d2a8471SDag-Erling Smørgrav 6264ca1ab94SDag-Erling Smørgrav return cf; 6274ca1ab94SDag-Erling Smørgrav 6284ca1ab94SDag-Erling Smørgrav fouch: 6294ca1ab94SDag-Erling Smørgrav fclose(f); 6304ca1ab94SDag-Erling Smørgrav free(c); 6310fba3a00SDag-Erling Smørgrav _http_seterr(999); /* XXX do this properly RSN */ 6324ca1ab94SDag-Erling Smørgrav return NULL; 6333d2a8471SDag-Erling Smørgrav cfouch: 6343d2a8471SDag-Erling Smørgrav fclose(cf); 6353d2a8471SDag-Erling Smørgrav _http_seterr(999); /* XXX do this properly RSN */ 6363d2a8471SDag-Erling Smørgrav return NULL; 6374ca1ab94SDag-Erling Smørgrav } 6384ca1ab94SDag-Erling Smørgrav 6394ca1ab94SDag-Erling Smørgrav FILE * 640d8acd8dcSDag-Erling Smørgrav fetchPutHTTP(struct url *URL, char *flags) 6414ca1ab94SDag-Erling Smørgrav { 6424ca1ab94SDag-Erling Smørgrav warnx("fetchPutHTTP(): not implemented"); 6434ca1ab94SDag-Erling Smørgrav return NULL; 6444ca1ab94SDag-Erling Smørgrav } 645d8acd8dcSDag-Erling Smørgrav 646d8acd8dcSDag-Erling Smørgrav /* 647d8acd8dcSDag-Erling Smørgrav * Get an HTTP document's metadata 648d8acd8dcSDag-Erling Smørgrav */ 649d8acd8dcSDag-Erling Smørgrav int 65060245e42SDag-Erling Smørgrav fetchStatHTTP(struct url *URL, struct url_stat *us, char *flags) 651d8acd8dcSDag-Erling Smørgrav { 65235f723dbSDag-Erling Smørgrav int e, noredirect, proxy; 65360245e42SDag-Erling Smørgrav size_t len; 6546f9febf9SDag-Erling Smørgrav char *ln, *p, *q; 65560245e42SDag-Erling Smørgrav FILE *f; 65660245e42SDag-Erling Smørgrav 6576f9febf9SDag-Erling Smørgrav noredirect = (flags && strchr(flags, 'A')); 6586f9febf9SDag-Erling Smørgrav 659f8f4130bSDag-Erling Smørgrav us->size = -1; 660f8f4130bSDag-Erling Smørgrav us->atime = us->mtime = 0; 66160245e42SDag-Erling Smørgrav 66260245e42SDag-Erling Smørgrav /* connect */ 66335f723dbSDag-Erling Smørgrav if ((f = _http_connect(URL, flags, &proxy)) == NULL) 66460245e42SDag-Erling Smørgrav return -1; 66560245e42SDag-Erling Smørgrav 66635f723dbSDag-Erling Smørgrav e = _http_request(f, "HEAD", URL, flags, proxy); 6676f9febf9SDag-Erling Smørgrav if (e != HTTP_OK && (e != HTTP_MOVED || noredirect)) { 66860245e42SDag-Erling Smørgrav _http_seterr(e); 669cf5af79cSDag-Erling Smørgrav fclose(f); 670cf5af79cSDag-Erling Smørgrav return -1; 67160245e42SDag-Erling Smørgrav } 67260245e42SDag-Erling Smørgrav 67360245e42SDag-Erling Smørgrav while (1) { 67460245e42SDag-Erling Smørgrav if ((ln = fgetln(f, &len)) == NULL) 67560245e42SDag-Erling Smørgrav goto fouch; 67660245e42SDag-Erling Smørgrav if ((ln[0] == '\r') || (ln[0] == '\n')) 67760245e42SDag-Erling Smørgrav break; 67860245e42SDag-Erling Smørgrav while (isspace(ln[len-1])) 67960245e42SDag-Erling Smørgrav --len; 68060245e42SDag-Erling Smørgrav ln[len] = '\0'; /* XXX */ 68160245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln)); 6826f9febf9SDag-Erling Smørgrav if ((p = _http_match("Location", ln)) != NULL) { 6836f9febf9SDag-Erling Smørgrav struct url *url; 6846f9febf9SDag-Erling Smørgrav 6856f9febf9SDag-Erling Smørgrav for (q = p; *q && !isspace(*q); q++) 6866f9febf9SDag-Erling Smørgrav /* VOID */ ; 6876f9febf9SDag-Erling Smørgrav *q = 0; 6886f9febf9SDag-Erling Smørgrav if ((url = fetchParseURL(p)) == NULL) 6896f9febf9SDag-Erling Smørgrav goto ouch; 6906f9febf9SDag-Erling Smørgrav url->offset = URL->offset; 6916f9febf9SDag-Erling Smørgrav url->length = URL->length; 6926f9febf9SDag-Erling Smørgrav DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p)); 6936f9febf9SDag-Erling Smørgrav e = fetchStatHTTP(url, us, flags); 6946f9febf9SDag-Erling Smørgrav fetchFreeURL(url); 6956f9febf9SDag-Erling Smørgrav fclose(f); 6966f9febf9SDag-Erling Smørgrav return e; 6976f9febf9SDag-Erling Smørgrav } else if ((p = _http_match("Last-Modified", ln)) != NULL) { 69860245e42SDag-Erling Smørgrav struct tm tm; 69960245e42SDag-Erling Smørgrav char locale[64]; 70060245e42SDag-Erling Smørgrav 70160245e42SDag-Erling Smørgrav strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale); 70260245e42SDag-Erling Smørgrav setlocale(LC_TIME, "C"); 70360245e42SDag-Erling Smørgrav strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 70460245e42SDag-Erling Smørgrav /* XXX should add support for date-2 and date-3 */ 70560245e42SDag-Erling Smørgrav setlocale(LC_TIME, locale); 70660245e42SDag-Erling Smørgrav us->atime = us->mtime = timegm(&tm); 70760245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 70860245e42SDag-Erling Smørgrav "%02d:%02d:%02d\033[m]\n", 70960245e42SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 71060245e42SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 71160245e42SDag-Erling Smørgrav } else if ((p = _http_match("Content-Length", ln)) != NULL) { 71260245e42SDag-Erling Smørgrav us->size = 0; 71360245e42SDag-Erling Smørgrav while (*p && isdigit(*p)) 71460245e42SDag-Erling Smørgrav us->size = us->size * 10 + (*p++ - '0'); 71560245e42SDag-Erling Smørgrav DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", us->size)); 71660245e42SDag-Erling Smørgrav } 71760245e42SDag-Erling Smørgrav } 71860245e42SDag-Erling Smørgrav 719f8f4130bSDag-Erling Smørgrav fclose(f); 72060245e42SDag-Erling Smørgrav return 0; 72160245e42SDag-Erling Smørgrav ouch: 72260245e42SDag-Erling Smørgrav _http_seterr(999); /* XXX do this properly RSN */ 72360245e42SDag-Erling Smørgrav fouch: 72460245e42SDag-Erling Smørgrav fclose(f); 725d8acd8dcSDag-Erling Smørgrav return -1; 726d8acd8dcSDag-Erling Smørgrav } 727ce71b736SDag-Erling Smørgrav 728ce71b736SDag-Erling Smørgrav /* 729ce71b736SDag-Erling Smørgrav * List a directory 730ce71b736SDag-Erling Smørgrav */ 731ce71b736SDag-Erling Smørgrav struct url_ent * 732ce71b736SDag-Erling Smørgrav fetchListHTTP(struct url *url, char *flags) 733ce71b736SDag-Erling Smørgrav { 734ce71b736SDag-Erling Smørgrav warnx("fetchListHTTP(): not implemented"); 735ce71b736SDag-Erling Smørgrav return NULL; 736ce71b736SDag-Erling Smørgrav } 737