14ca1ab94SDag-Erling Smørgrav /*- 2*e4878e39SDag-Erling Smørgrav * Copyright (c) 2000 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 15*e4878e39SDag-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 314ca1ab94SDag-Erling Smørgrav #include <sys/param.h> 3228c645cfSHajimu UMEMOTO #include <sys/socket.h> 334ca1ab94SDag-Erling Smørgrav 344ca1ab94SDag-Erling Smørgrav #include <ctype.h> 35*e4878e39SDag-Erling Smørgrav #include <err.h> 36*e4878e39SDag-Erling Smørgrav #include <errno.h> 3760245e42SDag-Erling Smørgrav #include <locale.h> 38e6182307SDag-Erling Smørgrav #include <netdb.h> 39f62e5228SDag-Erling Smørgrav #include <stdarg.h> 404ca1ab94SDag-Erling Smørgrav #include <stdio.h> 414ca1ab94SDag-Erling Smørgrav #include <stdlib.h> 424ca1ab94SDag-Erling Smørgrav #include <string.h> 4360245e42SDag-Erling Smørgrav #include <time.h> 444ca1ab94SDag-Erling Smørgrav #include <unistd.h> 454ca1ab94SDag-Erling Smørgrav 464ca1ab94SDag-Erling Smørgrav #include "fetch.h" 47842a95ccSDag-Erling Smørgrav #include "common.h" 480fba3a00SDag-Erling Smørgrav #include "httperr.h" 494ca1ab94SDag-Erling Smørgrav 50*e4878e39SDag-Erling Smørgrav extern char *__progname; /* XXX not portable */ 514ca1ab94SDag-Erling Smørgrav 52*e4878e39SDag-Erling Smørgrav /* Maximum number of redirects to follow */ 53*e4878e39SDag-Erling Smørgrav #define MAX_REDIRECT 5 544ca1ab94SDag-Erling Smørgrav 55*e4878e39SDag-Erling Smørgrav /* Symbolic names for reply codes we care about */ 563d2a8471SDag-Erling Smørgrav #define HTTP_OK 200 573d2a8471SDag-Erling Smørgrav #define HTTP_PARTIAL 206 58*e4878e39SDag-Erling Smørgrav #define HTTP_MOVED_PERM 301 59*e4878e39SDag-Erling Smørgrav #define HTTP_MOVED_TEMP 302 60*e4878e39SDag-Erling Smørgrav #define HTTP_SEE_OTHER 303 61*e4878e39SDag-Erling Smørgrav #define HTTP_NEED_AUTH 401 62*e4878e39SDag-Erling Smørgrav #define HTTP_NEED_PROXY_AUTH 403 63*e4878e39SDag-Erling Smørgrav #define HTTP_PROTOCOL_ERROR 999 64*e4878e39SDag-Erling Smørgrav 65*e4878e39SDag-Erling Smørgrav #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ 66*e4878e39SDag-Erling Smørgrav || (xyz) == HTTP_MOVED_TEMP \ 67*e4878e39SDag-Erling Smørgrav || (xyz) == HTTP_SEE_OTHER) 68*e4878e39SDag-Erling Smørgrav 69*e4878e39SDag-Erling Smørgrav 70*e4878e39SDag-Erling Smørgrav 71*e4878e39SDag-Erling Smørgrav /***************************************************************************** 72*e4878e39SDag-Erling Smørgrav * I/O functions for decoding chunked streams 73*e4878e39SDag-Erling Smørgrav */ 743d2a8471SDag-Erling Smørgrav 754ca1ab94SDag-Erling Smørgrav struct cookie 764ca1ab94SDag-Erling Smørgrav { 77*e4878e39SDag-Erling Smørgrav int fd; 784ca1ab94SDag-Erling Smørgrav char *buf; 79*e4878e39SDag-Erling Smørgrav size_t b_size; 80*e4878e39SDag-Erling Smørgrav size_t b_len; 81*e4878e39SDag-Erling Smørgrav int b_pos; 82*e4878e39SDag-Erling Smørgrav int eof; 83*e4878e39SDag-Erling Smørgrav int error; 84*e4878e39SDag-Erling Smørgrav long chunksize; 85*e4878e39SDag-Erling Smørgrav #ifdef DEBUG 86*e4878e39SDag-Erling Smørgrav long total; 87*e4878e39SDag-Erling Smørgrav #endif 884ca1ab94SDag-Erling Smørgrav }; 894ca1ab94SDag-Erling Smørgrav 90f62e5228SDag-Erling Smørgrav /* 91*e4878e39SDag-Erling Smørgrav * Get next chunk header 92f62e5228SDag-Erling Smørgrav */ 93f62e5228SDag-Erling Smørgrav static int 94*e4878e39SDag-Erling Smørgrav _http_new_chunk(struct cookie *c) 95f62e5228SDag-Erling Smørgrav { 96*e4878e39SDag-Erling Smørgrav char *p; 97f62e5228SDag-Erling Smørgrav 98*e4878e39SDag-Erling Smørgrav if (_fetch_getln(c->fd, &c->buf, &c->b_size, &c->b_len) == -1) 99*e4878e39SDag-Erling Smørgrav return -1; 100f62e5228SDag-Erling Smørgrav 101*e4878e39SDag-Erling Smørgrav if (c->b_len < 2 || !ishexnumber(*c->buf)) 102*e4878e39SDag-Erling Smørgrav return -1; 103*e4878e39SDag-Erling Smørgrav 104*e4878e39SDag-Erling Smørgrav for (p = c->buf; !isspace(*p) && *p != ';' && p < c->buf + c->b_len; ++p) 105*e4878e39SDag-Erling Smørgrav if (!ishexnumber(*p)) 106*e4878e39SDag-Erling Smørgrav return -1; 107*e4878e39SDag-Erling Smørgrav else if (isdigit(*p)) 108*e4878e39SDag-Erling Smørgrav c->chunksize = c->chunksize * 16 + *p - '0'; 109*e4878e39SDag-Erling Smørgrav else 110*e4878e39SDag-Erling Smørgrav c->chunksize = c->chunksize * 16 + 10 + tolower(*p) - 'a'; 111*e4878e39SDag-Erling Smørgrav 112*e4878e39SDag-Erling Smørgrav #ifdef DEBUG 113*e4878e39SDag-Erling Smørgrav c->total += c->chunksize; 114*e4878e39SDag-Erling Smørgrav if (c->chunksize == 0) 115*e4878e39SDag-Erling Smørgrav fprintf(stderr, "\033[1m_http_fillbuf(): " 116*e4878e39SDag-Erling Smørgrav "end of last chunk\033[m\n"); 117*e4878e39SDag-Erling Smørgrav else 118*e4878e39SDag-Erling Smørgrav fprintf(stderr, "\033[1m_http_fillbuf(): " 119*e4878e39SDag-Erling Smørgrav "new chunk: %ld (%ld)\033[m\n", c->chunksize, c->total); 120*e4878e39SDag-Erling Smørgrav #endif 121*e4878e39SDag-Erling Smørgrav 122*e4878e39SDag-Erling Smørgrav return c->chunksize; 123f62e5228SDag-Erling Smørgrav } 124f62e5228SDag-Erling Smørgrav 125f62e5228SDag-Erling Smørgrav /* 126f62e5228SDag-Erling Smørgrav * Fill the input buffer, do chunk decoding on the fly 127f62e5228SDag-Erling Smørgrav */ 128*e4878e39SDag-Erling Smørgrav static int 1294ca1ab94SDag-Erling Smørgrav _http_fillbuf(struct cookie *c) 1304ca1ab94SDag-Erling Smørgrav { 131*e4878e39SDag-Erling Smørgrav if (c->error) 132*e4878e39SDag-Erling Smørgrav return -1; 1334ca1ab94SDag-Erling Smørgrav if (c->eof) 134*e4878e39SDag-Erling Smørgrav return 0; 1354ca1ab94SDag-Erling Smørgrav 1364ca1ab94SDag-Erling Smørgrav if (c->chunksize == 0) { 137*e4878e39SDag-Erling Smørgrav switch (_http_new_chunk(c)) { 138*e4878e39SDag-Erling Smørgrav case -1: 139*e4878e39SDag-Erling Smørgrav c->error = 1; 140*e4878e39SDag-Erling Smørgrav return -1; 141*e4878e39SDag-Erling Smørgrav case 0: 1424ca1ab94SDag-Erling Smørgrav c->eof = 1; 143*e4878e39SDag-Erling Smørgrav return 0; 1444ca1ab94SDag-Erling Smørgrav } 1454ca1ab94SDag-Erling Smørgrav } 146*e4878e39SDag-Erling Smørgrav 147*e4878e39SDag-Erling Smørgrav if (c->b_size < c->chunksize) { 148*e4878e39SDag-Erling Smørgrav char *tmp; 149*e4878e39SDag-Erling Smørgrav 150*e4878e39SDag-Erling Smørgrav if ((tmp = realloc(c->buf, c->chunksize)) == NULL) 151*e4878e39SDag-Erling Smørgrav return -1; 152*e4878e39SDag-Erling Smørgrav c->buf = tmp; 153*e4878e39SDag-Erling Smørgrav c->b_size = c->chunksize; 154*e4878e39SDag-Erling Smørgrav } 155*e4878e39SDag-Erling Smørgrav 156*e4878e39SDag-Erling Smørgrav if ((c->b_len = read(c->fd, c->buf, c->chunksize)) == -1) 157*e4878e39SDag-Erling Smørgrav return -1; 1584ca1ab94SDag-Erling Smørgrav c->chunksize -= c->b_len; 159*e4878e39SDag-Erling Smørgrav 160*e4878e39SDag-Erling Smørgrav if (c->chunksize == 0) { 161*e4878e39SDag-Erling Smørgrav char endl[2]; 162*e4878e39SDag-Erling Smørgrav read(c->fd, endl, 2); 1634ca1ab94SDag-Erling Smørgrav } 164*e4878e39SDag-Erling Smørgrav 165*e4878e39SDag-Erling Smørgrav c->b_pos = 0; 166*e4878e39SDag-Erling Smørgrav 167*e4878e39SDag-Erling Smørgrav return c->b_len; 1684ca1ab94SDag-Erling Smørgrav } 1694ca1ab94SDag-Erling Smørgrav 170f62e5228SDag-Erling Smørgrav /* 171f62e5228SDag-Erling Smørgrav * Read function 172f62e5228SDag-Erling Smørgrav */ 1734ca1ab94SDag-Erling Smørgrav static int 174*e4878e39SDag-Erling Smørgrav _http_readfn(void *v, char *buf, int len) 1754ca1ab94SDag-Erling Smørgrav { 176*e4878e39SDag-Erling Smørgrav struct cookie *c = (struct cookie *)v; 177*e4878e39SDag-Erling Smørgrav int l, pos; 1784ca1ab94SDag-Erling Smørgrav 179*e4878e39SDag-Erling Smørgrav if (c->error) 180*e4878e39SDag-Erling Smørgrav return -1; 181*e4878e39SDag-Erling Smørgrav if (c->eof) 182*e4878e39SDag-Erling Smørgrav return 0; 183*e4878e39SDag-Erling Smørgrav 184*e4878e39SDag-Erling Smørgrav for (pos = 0; len > 0; pos += l, len -= l) { 185*e4878e39SDag-Erling Smørgrav /* empty buffer */ 186*e4878e39SDag-Erling Smørgrav if (!c->buf || c->b_pos == c->b_len) 187*e4878e39SDag-Erling Smørgrav if (_http_fillbuf(c) < 1) 188*e4878e39SDag-Erling Smørgrav break; 189*e4878e39SDag-Erling Smørgrav l = c->b_len - c->b_pos; 190*e4878e39SDag-Erling Smørgrav if (len < l) 191*e4878e39SDag-Erling Smørgrav l = len; 192*e4878e39SDag-Erling Smørgrav bcopy(c->buf + c->b_pos, buf + pos, l); 193*e4878e39SDag-Erling Smørgrav c->b_pos += l; 1944ca1ab94SDag-Erling Smørgrav } 1954ca1ab94SDag-Erling Smørgrav 196*e4878e39SDag-Erling Smørgrav if (!pos && c->error) 1974ca1ab94SDag-Erling Smørgrav return -1; 198*e4878e39SDag-Erling Smørgrav return pos; 1994ca1ab94SDag-Erling Smørgrav } 2004ca1ab94SDag-Erling Smørgrav 201f62e5228SDag-Erling Smørgrav /* 202f62e5228SDag-Erling Smørgrav * Write function 203f62e5228SDag-Erling Smørgrav */ 2044ca1ab94SDag-Erling Smørgrav static int 205*e4878e39SDag-Erling Smørgrav _http_writefn(void *v, const char *buf, int len) 2064ca1ab94SDag-Erling Smørgrav { 207*e4878e39SDag-Erling Smørgrav struct cookie *c = (struct cookie *)v; 208*e4878e39SDag-Erling Smørgrav 209*e4878e39SDag-Erling Smørgrav return write(c->fd, buf, len); 2104ca1ab94SDag-Erling Smørgrav } 2114ca1ab94SDag-Erling Smørgrav 212f62e5228SDag-Erling Smørgrav /* 213f62e5228SDag-Erling Smørgrav * Close function 214f62e5228SDag-Erling Smørgrav */ 2154ca1ab94SDag-Erling Smørgrav static int 216*e4878e39SDag-Erling Smørgrav _http_closefn(void *v) 2174ca1ab94SDag-Erling Smørgrav { 218*e4878e39SDag-Erling Smørgrav struct cookie *c = (struct cookie *)v; 219*e4878e39SDag-Erling Smørgrav int r; 220*e4878e39SDag-Erling Smørgrav 221*e4878e39SDag-Erling Smørgrav r = close(c->fd); 222*e4878e39SDag-Erling Smørgrav if (c->buf) 223*e4878e39SDag-Erling Smørgrav free(c->buf); 2244ca1ab94SDag-Erling Smørgrav free(c); 225*e4878e39SDag-Erling Smørgrav return r; 2264ca1ab94SDag-Erling Smørgrav } 2274ca1ab94SDag-Erling Smørgrav 228f62e5228SDag-Erling Smørgrav /* 229*e4878e39SDag-Erling Smørgrav * Wrap a file descriptor up 230f62e5228SDag-Erling Smørgrav */ 231*e4878e39SDag-Erling Smørgrav static FILE * 232*e4878e39SDag-Erling Smørgrav _http_funopen(int fd) 2334ca1ab94SDag-Erling Smørgrav { 234*e4878e39SDag-Erling Smørgrav struct cookie *c; 235*e4878e39SDag-Erling Smørgrav FILE *f; 236*e4878e39SDag-Erling Smørgrav 237*e4878e39SDag-Erling Smørgrav if ((c = calloc(1, sizeof *c)) == NULL) { 238*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 239*e4878e39SDag-Erling Smørgrav return NULL; 2404ca1ab94SDag-Erling Smørgrav } 241*e4878e39SDag-Erling Smørgrav c->fd = fd; 242*e4878e39SDag-Erling Smørgrav if (!(f = funopen(c, _http_readfn, _http_writefn, NULL, _http_closefn))) { 243*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 244*e4878e39SDag-Erling Smørgrav free(c); 245*e4878e39SDag-Erling Smørgrav return NULL; 246*e4878e39SDag-Erling Smørgrav } 247*e4878e39SDag-Erling Smørgrav return f; 248*e4878e39SDag-Erling Smørgrav } 249*e4878e39SDag-Erling Smørgrav 250*e4878e39SDag-Erling Smørgrav 251*e4878e39SDag-Erling Smørgrav /***************************************************************************** 252*e4878e39SDag-Erling Smørgrav * Helper functions for talking to the server and parsing its replies 253*e4878e39SDag-Erling Smørgrav */ 254*e4878e39SDag-Erling Smørgrav 255*e4878e39SDag-Erling Smørgrav /* Header types */ 256*e4878e39SDag-Erling Smørgrav typedef enum { 257*e4878e39SDag-Erling Smørgrav hdr_syserror = -2, 258*e4878e39SDag-Erling Smørgrav hdr_error = -1, 259*e4878e39SDag-Erling Smørgrav hdr_end = 0, 260*e4878e39SDag-Erling Smørgrav hdr_unknown = 1, 261*e4878e39SDag-Erling Smørgrav hdr_content_length, 262*e4878e39SDag-Erling Smørgrav hdr_content_range, 263*e4878e39SDag-Erling Smørgrav hdr_last_modified, 264*e4878e39SDag-Erling Smørgrav hdr_location, 265*e4878e39SDag-Erling Smørgrav hdr_transfer_encoding 266*e4878e39SDag-Erling Smørgrav } hdr; 267*e4878e39SDag-Erling Smørgrav 268*e4878e39SDag-Erling Smørgrav /* Names of interesting headers */ 269*e4878e39SDag-Erling Smørgrav static struct { 270*e4878e39SDag-Erling Smørgrav hdr num; 271*e4878e39SDag-Erling Smørgrav char *name; 272*e4878e39SDag-Erling Smørgrav } hdr_names[] = { 273*e4878e39SDag-Erling Smørgrav { hdr_content_length, "Content-Length" }, 274*e4878e39SDag-Erling Smørgrav { hdr_content_range, "Content-Range" }, 275*e4878e39SDag-Erling Smørgrav { hdr_last_modified, "Last-Modified" }, 276*e4878e39SDag-Erling Smørgrav { hdr_location, "Location" }, 277*e4878e39SDag-Erling Smørgrav { hdr_transfer_encoding, "Transfer-Encoding" }, 278*e4878e39SDag-Erling Smørgrav { hdr_unknown, NULL }, 279*e4878e39SDag-Erling Smørgrav }; 280*e4878e39SDag-Erling Smørgrav 281*e4878e39SDag-Erling Smørgrav static char *reply_buf; 282*e4878e39SDag-Erling Smørgrav static size_t reply_size; 283*e4878e39SDag-Erling Smørgrav static size_t reply_length; 284*e4878e39SDag-Erling Smørgrav 285*e4878e39SDag-Erling Smørgrav /* 286*e4878e39SDag-Erling Smørgrav * Send a formatted line; optionally echo to terminal 287*e4878e39SDag-Erling Smørgrav */ 288*e4878e39SDag-Erling Smørgrav static int 289*e4878e39SDag-Erling Smørgrav _http_cmd(int fd, char *fmt, ...) 290*e4878e39SDag-Erling Smørgrav { 291*e4878e39SDag-Erling Smørgrav va_list ap; 292*e4878e39SDag-Erling Smørgrav size_t len; 293*e4878e39SDag-Erling Smørgrav char *msg; 294*e4878e39SDag-Erling Smørgrav int r; 295*e4878e39SDag-Erling Smørgrav 296*e4878e39SDag-Erling Smørgrav va_start(ap, fmt); 297*e4878e39SDag-Erling Smørgrav len = vasprintf(&msg, fmt, ap); 298*e4878e39SDag-Erling Smørgrav va_end(ap); 299*e4878e39SDag-Erling Smørgrav 300*e4878e39SDag-Erling Smørgrav if (msg == NULL) { 301*e4878e39SDag-Erling Smørgrav errno = ENOMEM; 302*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 303*e4878e39SDag-Erling Smørgrav return -1; 304*e4878e39SDag-Erling Smørgrav } 305*e4878e39SDag-Erling Smørgrav 306*e4878e39SDag-Erling Smørgrav r = _fetch_putln(fd, msg, len); 307*e4878e39SDag-Erling Smørgrav free(msg); 308*e4878e39SDag-Erling Smørgrav 309*e4878e39SDag-Erling Smørgrav if (r == -1) { 310*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 311*e4878e39SDag-Erling Smørgrav return -1; 312*e4878e39SDag-Erling Smørgrav } 313*e4878e39SDag-Erling Smørgrav 314*e4878e39SDag-Erling Smørgrav return 0; 315*e4878e39SDag-Erling Smørgrav } 316*e4878e39SDag-Erling Smørgrav 317*e4878e39SDag-Erling Smørgrav /* 318*e4878e39SDag-Erling Smørgrav * Get and parse status line 319*e4878e39SDag-Erling Smørgrav */ 320*e4878e39SDag-Erling Smørgrav static int 321*e4878e39SDag-Erling Smørgrav _http_get_reply(int fd) 322*e4878e39SDag-Erling Smørgrav { 323*e4878e39SDag-Erling Smørgrav if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1) 324*e4878e39SDag-Erling Smørgrav return -1; 325*e4878e39SDag-Erling Smørgrav /* 326*e4878e39SDag-Erling Smørgrav * A valid status line looks like "HTTP/m.n xyz reason" where m 327*e4878e39SDag-Erling Smørgrav * and n are the major and minor protocol version numbers and xyz 328*e4878e39SDag-Erling Smørgrav * is the reply code. 329*e4878e39SDag-Erling Smørgrav * We grok HTTP 1.0 and 1.1, so m must be 1 and n must be 0 or 1. 330*e4878e39SDag-Erling Smørgrav * We don't care about the reason phrase. 331*e4878e39SDag-Erling Smørgrav */ 332*e4878e39SDag-Erling Smørgrav if (strncmp(reply_buf, "HTTP/1.", 7) != 0 333*e4878e39SDag-Erling Smørgrav || (reply_buf[7] != '0' && reply_buf[7] != '1') || reply_buf[8] != ' ' 334*e4878e39SDag-Erling Smørgrav || !isdigit(reply_buf[9]) 335*e4878e39SDag-Erling Smørgrav || !isdigit(reply_buf[10]) 336*e4878e39SDag-Erling Smørgrav || !isdigit(reply_buf[11])) 337*e4878e39SDag-Erling Smørgrav return HTTP_PROTOCOL_ERROR; 338*e4878e39SDag-Erling Smørgrav 339*e4878e39SDag-Erling Smørgrav return ((reply_buf[9] - '0') * 100 340*e4878e39SDag-Erling Smørgrav + (reply_buf[10] - '0') * 10 341*e4878e39SDag-Erling Smørgrav + (reply_buf[11] - '0')); 342*e4878e39SDag-Erling Smørgrav } 343*e4878e39SDag-Erling Smørgrav 344*e4878e39SDag-Erling Smørgrav /* 345*e4878e39SDag-Erling Smørgrav * Check a header; if the type matches the given string, return a 346*e4878e39SDag-Erling Smørgrav * pointer to the beginning of the value. 347*e4878e39SDag-Erling Smørgrav */ 348*e4878e39SDag-Erling Smørgrav static char * 349*e4878e39SDag-Erling Smørgrav _http_match(char *str, char *hdr) 350*e4878e39SDag-Erling Smørgrav { 351*e4878e39SDag-Erling Smørgrav while (*str && *hdr && tolower(*str++) == tolower(*hdr++)) 352*e4878e39SDag-Erling Smørgrav /* nothing */; 353*e4878e39SDag-Erling Smørgrav if (*str || *hdr != ':') 354*e4878e39SDag-Erling Smørgrav return NULL; 355*e4878e39SDag-Erling Smørgrav while (*hdr && isspace(*++hdr)) 356*e4878e39SDag-Erling Smørgrav /* nothing */; 357*e4878e39SDag-Erling Smørgrav return hdr; 358*e4878e39SDag-Erling Smørgrav } 359*e4878e39SDag-Erling Smørgrav 360*e4878e39SDag-Erling Smørgrav /* 361*e4878e39SDag-Erling Smørgrav * Get the next header and return the appropriate symbolic code. 362*e4878e39SDag-Erling Smørgrav */ 363*e4878e39SDag-Erling Smørgrav static hdr 364*e4878e39SDag-Erling Smørgrav _http_next_header(int fd, char **p) 365*e4878e39SDag-Erling Smørgrav { 366*e4878e39SDag-Erling Smørgrav int i; 367*e4878e39SDag-Erling Smørgrav 368*e4878e39SDag-Erling Smørgrav if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1) 369*e4878e39SDag-Erling Smørgrav return hdr_syserror; 370*e4878e39SDag-Erling Smørgrav while (reply_length && isspace(reply_buf[reply_length-1])) 371*e4878e39SDag-Erling Smørgrav reply_length--; 372*e4878e39SDag-Erling Smørgrav reply_buf[reply_length] = 0; 373*e4878e39SDag-Erling Smørgrav if (reply_length == 0) 374*e4878e39SDag-Erling Smørgrav return hdr_end; 375*e4878e39SDag-Erling Smørgrav /* 376*e4878e39SDag-Erling Smørgrav * We could check for malformed headers but we don't really care. 377*e4878e39SDag-Erling Smørgrav * A valid header starts with a token immediately followed by a 378*e4878e39SDag-Erling Smørgrav * colon; a token is any sequence of non-control, non-whitespace 379*e4878e39SDag-Erling Smørgrav * characters except "()<>@,;:\\\"{}". 380*e4878e39SDag-Erling Smørgrav */ 381*e4878e39SDag-Erling Smørgrav for (i = 0; hdr_names[i].num != hdr_unknown; i++) 382*e4878e39SDag-Erling Smørgrav if ((*p = _http_match(hdr_names[i].name, reply_buf)) != NULL) 383*e4878e39SDag-Erling Smørgrav return hdr_names[i].num; 384*e4878e39SDag-Erling Smørgrav return hdr_unknown; 385*e4878e39SDag-Erling Smørgrav } 386*e4878e39SDag-Erling Smørgrav 387*e4878e39SDag-Erling Smørgrav /* 388*e4878e39SDag-Erling Smørgrav * Parse a last-modified header 389*e4878e39SDag-Erling Smørgrav */ 390*e4878e39SDag-Erling Smørgrav static time_t 391*e4878e39SDag-Erling Smørgrav _http_parse_mtime(char *p) 392*e4878e39SDag-Erling Smørgrav { 393*e4878e39SDag-Erling Smørgrav char locale[64]; 394*e4878e39SDag-Erling Smørgrav struct tm tm; 395*e4878e39SDag-Erling Smørgrav 396*e4878e39SDag-Erling Smørgrav strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale); 397*e4878e39SDag-Erling Smørgrav setlocale(LC_TIME, "C"); 398*e4878e39SDag-Erling Smørgrav strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 399*e4878e39SDag-Erling Smørgrav /* XXX should add support for date-2 and date-3 */ 400*e4878e39SDag-Erling Smørgrav setlocale(LC_TIME, locale); 401*e4878e39SDag-Erling Smørgrav DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 402*e4878e39SDag-Erling Smørgrav "%02d:%02d:%02d\033[m]\n", 403*e4878e39SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 404*e4878e39SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 405*e4878e39SDag-Erling Smørgrav return timegm(&tm); 406*e4878e39SDag-Erling Smørgrav } 407*e4878e39SDag-Erling Smørgrav 408*e4878e39SDag-Erling Smørgrav /* 409*e4878e39SDag-Erling Smørgrav * Parse a content-length header 410*e4878e39SDag-Erling Smørgrav */ 411*e4878e39SDag-Erling Smørgrav static off_t 412*e4878e39SDag-Erling Smørgrav _http_parse_length(char *p) 413*e4878e39SDag-Erling Smørgrav { 414*e4878e39SDag-Erling Smørgrav off_t len; 415*e4878e39SDag-Erling Smørgrav 416*e4878e39SDag-Erling Smørgrav for (len = 0; *p && isdigit(*p); ++p) 417*e4878e39SDag-Erling Smørgrav len = len * 10 + (*p - '0'); 418*e4878e39SDag-Erling Smørgrav DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", len)); 419*e4878e39SDag-Erling Smørgrav return len; 420*e4878e39SDag-Erling Smørgrav } 421*e4878e39SDag-Erling Smørgrav 422*e4878e39SDag-Erling Smørgrav /* 423*e4878e39SDag-Erling Smørgrav * Parse a content-range header 424*e4878e39SDag-Erling Smørgrav */ 425*e4878e39SDag-Erling Smørgrav static off_t 426*e4878e39SDag-Erling Smørgrav _http_parse_range(char *p) 427*e4878e39SDag-Erling Smørgrav { 428*e4878e39SDag-Erling Smørgrav off_t off; 429*e4878e39SDag-Erling Smørgrav 430*e4878e39SDag-Erling Smørgrav if (strncasecmp(p, "bytes ", 6) != 0) 431*e4878e39SDag-Erling Smørgrav return -1; 432*e4878e39SDag-Erling Smørgrav for (p += 6, off = 0; *p && isdigit(*p); ++p) 433*e4878e39SDag-Erling Smørgrav off = off * 10 + *p - '0'; 434*e4878e39SDag-Erling Smørgrav if (*p != '-') 435*e4878e39SDag-Erling Smørgrav return -1; 436*e4878e39SDag-Erling Smørgrav DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", off)); 437*e4878e39SDag-Erling Smørgrav return off; 438*e4878e39SDag-Erling Smørgrav } 439*e4878e39SDag-Erling Smørgrav 440*e4878e39SDag-Erling Smørgrav 441*e4878e39SDag-Erling Smørgrav /***************************************************************************** 442*e4878e39SDag-Erling Smørgrav * Helper functions for authorization 443*e4878e39SDag-Erling Smørgrav */ 4444ca1ab94SDag-Erling Smørgrav 445f62e5228SDag-Erling Smørgrav /* 446f62e5228SDag-Erling Smørgrav * Base64 encoding 447f62e5228SDag-Erling Smørgrav */ 44835f723dbSDag-Erling Smørgrav static char * 44935f723dbSDag-Erling Smørgrav _http_base64(char *src) 450f62e5228SDag-Erling Smørgrav { 451f62e5228SDag-Erling Smørgrav static const char base64[] = 452f62e5228SDag-Erling Smørgrav "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 453f62e5228SDag-Erling Smørgrav "abcdefghijklmnopqrstuvwxyz" 454f62e5228SDag-Erling Smørgrav "0123456789+/"; 45535f723dbSDag-Erling Smørgrav char *str, *dst; 45635f723dbSDag-Erling Smørgrav size_t l; 45735f723dbSDag-Erling Smørgrav int t, r; 45835f723dbSDag-Erling Smørgrav 45935f723dbSDag-Erling Smørgrav l = strlen(src); 46035f723dbSDag-Erling Smørgrav if ((str = malloc(((l + 2) / 3) * 4)) == NULL) 46135f723dbSDag-Erling Smørgrav return NULL; 46235f723dbSDag-Erling Smørgrav dst = str; 46335f723dbSDag-Erling Smørgrav r = 0; 464f62e5228SDag-Erling Smørgrav 465f62e5228SDag-Erling Smørgrav while (l >= 3) { 466f62e5228SDag-Erling Smørgrav t = (src[0] << 16) | (src[1] << 8) | src[2]; 467f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 468f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 469f62e5228SDag-Erling Smørgrav dst[2] = base64[(t >> 6) & 0x3f]; 470f62e5228SDag-Erling Smørgrav dst[3] = base64[(t >> 0) & 0x3f]; 471f62e5228SDag-Erling Smørgrav src += 3; l -= 3; 472f62e5228SDag-Erling Smørgrav dst += 4; r += 4; 473f62e5228SDag-Erling Smørgrav } 474f62e5228SDag-Erling Smørgrav 475f62e5228SDag-Erling Smørgrav switch (l) { 476f62e5228SDag-Erling Smørgrav case 2: 477f62e5228SDag-Erling Smørgrav t = (src[0] << 16) | (src[1] << 8); 478f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 479f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 480f62e5228SDag-Erling Smørgrav dst[2] = base64[(t >> 6) & 0x3f]; 481f62e5228SDag-Erling Smørgrav dst[3] = '='; 482f62e5228SDag-Erling Smørgrav dst += 4; 483f62e5228SDag-Erling Smørgrav r += 4; 484f62e5228SDag-Erling Smørgrav break; 485f62e5228SDag-Erling Smørgrav case 1: 486f62e5228SDag-Erling Smørgrav t = src[0] << 16; 487f62e5228SDag-Erling Smørgrav dst[0] = base64[(t >> 18) & 0x3f]; 488f62e5228SDag-Erling Smørgrav dst[1] = base64[(t >> 12) & 0x3f]; 489f62e5228SDag-Erling Smørgrav dst[2] = dst[3] = '='; 490f62e5228SDag-Erling Smørgrav dst += 4; 491f62e5228SDag-Erling Smørgrav r += 4; 492f62e5228SDag-Erling Smørgrav break; 493f62e5228SDag-Erling Smørgrav case 0: 494f62e5228SDag-Erling Smørgrav break; 495f62e5228SDag-Erling Smørgrav } 496f62e5228SDag-Erling Smørgrav 497f62e5228SDag-Erling Smørgrav *dst = 0; 49835f723dbSDag-Erling Smørgrav return str; 499f62e5228SDag-Erling Smørgrav } 500f62e5228SDag-Erling Smørgrav 501f62e5228SDag-Erling Smørgrav /* 502f62e5228SDag-Erling Smørgrav * Encode username and password 503f62e5228SDag-Erling Smørgrav */ 50435f723dbSDag-Erling Smørgrav static int 505*e4878e39SDag-Erling Smørgrav _http_basic_auth(int fd, char *hdr, char *usr, char *pwd) 506f62e5228SDag-Erling Smørgrav { 50735f723dbSDag-Erling Smørgrav char *upw, *auth; 50835f723dbSDag-Erling Smørgrav int r; 509f62e5228SDag-Erling Smørgrav 51035f723dbSDag-Erling Smørgrav if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 51135f723dbSDag-Erling Smørgrav return -1; 51235f723dbSDag-Erling Smørgrav auth = _http_base64(upw); 51335f723dbSDag-Erling Smørgrav free(upw); 51435f723dbSDag-Erling Smørgrav if (auth == NULL) 51535f723dbSDag-Erling Smørgrav return -1; 516*e4878e39SDag-Erling Smørgrav r = _http_cmd(fd, "%s: Basic %s", hdr, auth); 51735f723dbSDag-Erling Smørgrav free(auth); 51835f723dbSDag-Erling Smørgrav return r; 5194d029f13SDag-Erling Smørgrav } 52035f723dbSDag-Erling Smørgrav 52135f723dbSDag-Erling Smørgrav /* 52235f723dbSDag-Erling Smørgrav * Send an authorization header 52335f723dbSDag-Erling Smørgrav */ 52435f723dbSDag-Erling Smørgrav static int 525*e4878e39SDag-Erling Smørgrav _http_authorize(int fd, char *hdr, char *p) 52635f723dbSDag-Erling Smørgrav { 52735f723dbSDag-Erling Smørgrav /* basic authorization */ 52835f723dbSDag-Erling Smørgrav if (strncasecmp(p, "basic:", 6) == 0) { 52935f723dbSDag-Erling Smørgrav char *user, *pwd, *str; 53035f723dbSDag-Erling Smørgrav int r; 53135f723dbSDag-Erling Smørgrav 53235f723dbSDag-Erling Smørgrav /* skip realm */ 53335f723dbSDag-Erling Smørgrav for (p += 6; *p && *p != ':'; ++p) 53435f723dbSDag-Erling Smørgrav /* nothing */ ; 53535f723dbSDag-Erling Smørgrav if (!*p || strchr(++p, ':') == NULL) 53635f723dbSDag-Erling Smørgrav return -1; 53735f723dbSDag-Erling Smørgrav if ((str = strdup(p)) == NULL) 53835f723dbSDag-Erling Smørgrav return -1; /* XXX */ 53935f723dbSDag-Erling Smørgrav user = str; 54035f723dbSDag-Erling Smørgrav pwd = strchr(str, ':'); 54135f723dbSDag-Erling Smørgrav *pwd++ = '\0'; 542*e4878e39SDag-Erling Smørgrav r = _http_basic_auth(fd, hdr, user, pwd); 54335f723dbSDag-Erling Smørgrav free(str); 54435f723dbSDag-Erling Smørgrav return r; 5454d029f13SDag-Erling Smørgrav } 54635f723dbSDag-Erling Smørgrav return -1; 547f62e5228SDag-Erling Smørgrav } 548f62e5228SDag-Erling Smørgrav 549*e4878e39SDag-Erling Smørgrav 550*e4878e39SDag-Erling Smørgrav /***************************************************************************** 551*e4878e39SDag-Erling Smørgrav * Helper functions for connecting to a server or proxy 552f62e5228SDag-Erling Smørgrav */ 553*e4878e39SDag-Erling Smørgrav 554*e4878e39SDag-Erling Smørgrav /* 555*e4878e39SDag-Erling Smørgrav * Connect to the specified HTTP proxy server. 556*e4878e39SDag-Erling Smørgrav */ 557*e4878e39SDag-Erling Smørgrav static int 558*e4878e39SDag-Erling Smørgrav _http_proxy_connect(char *proxy, int af, int verbose) 5594ca1ab94SDag-Erling Smørgrav { 560*e4878e39SDag-Erling Smørgrav char *hostname, *p; 561*e4878e39SDag-Erling Smørgrav int fd, port; 562*e4878e39SDag-Erling Smørgrav 563*e4878e39SDag-Erling Smørgrav /* get hostname */ 564*e4878e39SDag-Erling Smørgrav hostname = NULL; 56528c645cfSHajimu UMEMOTO #ifdef INET6 566*e4878e39SDag-Erling Smørgrav /* host part can be an IPv6 address enclosed in square brackets */ 567*e4878e39SDag-Erling Smørgrav if (*proxy == '[') { 568*e4878e39SDag-Erling Smørgrav if ((p = strchr(proxy, ']')) == NULL) { 569*e4878e39SDag-Erling Smørgrav /* no terminating bracket */ 570*e4878e39SDag-Erling Smørgrav /* XXX should set an error code */ 571*e4878e39SDag-Erling Smørgrav goto ouch; 572e6182307SDag-Erling Smørgrav } 573*e4878e39SDag-Erling Smørgrav if (p[1] != '\0' && p[1] != ':') { 574*e4878e39SDag-Erling Smørgrav /* garbage after address */ 575*e4878e39SDag-Erling Smørgrav /* XXX should set an error code */ 576*e4878e39SDag-Erling Smørgrav goto ouch; 577*e4878e39SDag-Erling Smørgrav } 578*e4878e39SDag-Erling Smørgrav if ((hostname = malloc(p - proxy)) == NULL) { 579*e4878e39SDag-Erling Smørgrav errno = ENOMEM; 580*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 581*e4878e39SDag-Erling Smørgrav goto ouch; 582*e4878e39SDag-Erling Smørgrav } 583*e4878e39SDag-Erling Smørgrav strncpy(hostname, proxy + 1, p - proxy - 1); 584*e4878e39SDag-Erling Smørgrav hostname[p - proxy - 1] = '\0'; 585*e4878e39SDag-Erling Smørgrav ++p; 586*e4878e39SDag-Erling Smørgrav } else { 587*e4878e39SDag-Erling Smørgrav #endif /* INET6 */ 588*e4878e39SDag-Erling Smørgrav if ((p = strchr(proxy, ':')) == NULL) 589*e4878e39SDag-Erling Smørgrav p = strchr(proxy, '\0'); 590*e4878e39SDag-Erling Smørgrav if ((hostname = malloc(p - proxy + 1)) == NULL) { 591*e4878e39SDag-Erling Smørgrav errno = ENOMEM; 592*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 593*e4878e39SDag-Erling Smørgrav goto ouch; 594*e4878e39SDag-Erling Smørgrav } 595*e4878e39SDag-Erling Smørgrav strncpy(hostname, proxy, p - proxy); 596*e4878e39SDag-Erling Smørgrav hostname[p - proxy] = '\0'; 59728c645cfSHajimu UMEMOTO #ifdef INET6 598e6182307SDag-Erling Smørgrav } 599*e4878e39SDag-Erling Smørgrav #endif /* INET6 */ 600*e4878e39SDag-Erling Smørgrav DEBUG(fprintf(stderr, "proxy name: [%s]\n", hostname)); 601*e4878e39SDag-Erling Smørgrav 602*e4878e39SDag-Erling Smørgrav /* get port number */ 603*e4878e39SDag-Erling Smørgrav port = 0; 604*e4878e39SDag-Erling Smørgrav if (*p == ':') { 605*e4878e39SDag-Erling Smørgrav ++p; 606*e4878e39SDag-Erling Smørgrav if (strspn(p, "0123456789") != strlen(p) || strlen(p) > 5) { 607*e4878e39SDag-Erling Smørgrav /* port number is non-numeric or too long */ 608*e4878e39SDag-Erling Smørgrav /* XXX should set an error code */ 609*e4878e39SDag-Erling Smørgrav goto ouch; 610*e4878e39SDag-Erling Smørgrav } 611*e4878e39SDag-Erling Smørgrav port = atoi(p); 612e6182307SDag-Erling Smørgrav if (port < 1 || port > 65535) { 613*e4878e39SDag-Erling Smørgrav /* port number is out of range */ 614*e4878e39SDag-Erling Smørgrav /* XXX should set an error code */ 615*e4878e39SDag-Erling Smørgrav goto ouch; 616e6182307SDag-Erling Smørgrav } 617e6182307SDag-Erling Smørgrav } 618*e4878e39SDag-Erling Smørgrav 619e6182307SDag-Erling Smørgrav if (!port) { 620e6182307SDag-Erling Smørgrav #if 0 621e6182307SDag-Erling Smørgrav /* 622e6182307SDag-Erling Smørgrav * commented out, since there is currently no service name 623e6182307SDag-Erling Smørgrav * for HTTP proxies 624e6182307SDag-Erling Smørgrav */ 625e6182307SDag-Erling Smørgrav struct servent *se; 626e6182307SDag-Erling Smørgrav 627e6182307SDag-Erling Smørgrav if ((se = getservbyname("xxxx", "tcp")) != NULL) 628e6182307SDag-Erling Smørgrav port = ntohs(se->s_port); 629e6182307SDag-Erling Smørgrav else 630e6182307SDag-Erling Smørgrav #endif 631e6182307SDag-Erling Smørgrav port = 3128; 632e6182307SDag-Erling Smørgrav } 633*e4878e39SDag-Erling Smørgrav DEBUG(fprintf(stderr, "proxy port: %d\n", port)); 6344ca1ab94SDag-Erling Smørgrav 6354ca1ab94SDag-Erling Smørgrav /* connect */ 636*e4878e39SDag-Erling Smørgrav if ((fd = _fetch_connect(hostname, port, af, verbose)) == -1) 637*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 638*e4878e39SDag-Erling Smørgrav return fd; 63960245e42SDag-Erling Smørgrav 64060245e42SDag-Erling Smørgrav ouch: 641*e4878e39SDag-Erling Smørgrav if (hostname) 642*e4878e39SDag-Erling Smørgrav free(hostname); 643*e4878e39SDag-Erling Smørgrav return -1; 64460245e42SDag-Erling Smørgrav } 64560245e42SDag-Erling Smørgrav 64660245e42SDag-Erling Smørgrav /* 647*e4878e39SDag-Erling Smørgrav * Connect to the correct HTTP server or proxy. 64860245e42SDag-Erling Smørgrav */ 64935f723dbSDag-Erling Smørgrav static int 650*e4878e39SDag-Erling Smørgrav _http_connect(struct url *URL, int *proxy, char *flags) 65160245e42SDag-Erling Smørgrav { 652*e4878e39SDag-Erling Smørgrav int direct, verbose; 653*e4878e39SDag-Erling Smørgrav int af, fd; 654*e4878e39SDag-Erling Smørgrav char *p; 655*e4878e39SDag-Erling Smørgrav 656*e4878e39SDag-Erling Smørgrav #ifdef INET6 657*e4878e39SDag-Erling Smørgrav af = AF_UNSPEC; 658*e4878e39SDag-Erling Smørgrav #else 659*e4878e39SDag-Erling Smørgrav af = AF_INET; 660*e4878e39SDag-Erling Smørgrav #endif 661*e4878e39SDag-Erling Smørgrav 662*e4878e39SDag-Erling Smørgrav direct = (flags && strchr(flags, 'd')); 663*e4878e39SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 664*e4878e39SDag-Erling Smørgrav if (flags && strchr(flags, '4')) 665*e4878e39SDag-Erling Smørgrav af = AF_INET; 666*e4878e39SDag-Erling Smørgrav else if (flags && strchr(flags, '6')) 667*e4878e39SDag-Erling Smørgrav af = AF_INET6; 668*e4878e39SDag-Erling Smørgrav 669*e4878e39SDag-Erling Smørgrav /* check port */ 670*e4878e39SDag-Erling Smørgrav if (!URL->port) { 671*e4878e39SDag-Erling Smørgrav struct servent *se; 672*e4878e39SDag-Erling Smørgrav 673*e4878e39SDag-Erling Smørgrav /* Scheme can be ftp if we're using a proxy */ 674*e4878e39SDag-Erling Smørgrav if (strcasecmp(URL->scheme, "ftp") == 0) 675*e4878e39SDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 676*e4878e39SDag-Erling Smørgrav URL->port = ntohs(se->s_port); 677*e4878e39SDag-Erling Smørgrav else 678*e4878e39SDag-Erling Smørgrav URL->port = 21; 679*e4878e39SDag-Erling Smørgrav else 680*e4878e39SDag-Erling Smørgrav if ((se = getservbyname("http", "tcp")) != NULL) 681*e4878e39SDag-Erling Smørgrav URL->port = ntohs(se->s_port); 682*e4878e39SDag-Erling Smørgrav else 683*e4878e39SDag-Erling Smørgrav URL->port = 80; 684*e4878e39SDag-Erling Smørgrav } 685*e4878e39SDag-Erling Smørgrav 686*e4878e39SDag-Erling Smørgrav if (!direct && (p = getenv("HTTP_PROXY")) != NULL) { 687*e4878e39SDag-Erling Smørgrav /* attempt to connect to proxy server */ 688*e4878e39SDag-Erling Smørgrav if ((fd = _http_proxy_connect(p, af, verbose)) == -1) 689*e4878e39SDag-Erling Smørgrav return -1; 690*e4878e39SDag-Erling Smørgrav *proxy = 1; 691*e4878e39SDag-Erling Smørgrav } else { 692*e4878e39SDag-Erling Smørgrav /* if no proxy is configured, try direct */ 693*e4878e39SDag-Erling Smørgrav if (strcasecmp(URL->scheme, "ftp") == 0) { 694*e4878e39SDag-Erling Smørgrav /* can't talk http to an ftp server */ 695*e4878e39SDag-Erling Smørgrav /* XXX should set an error code */ 696*e4878e39SDag-Erling Smørgrav return -1; 697*e4878e39SDag-Erling Smørgrav } 698*e4878e39SDag-Erling Smørgrav if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1) 699*e4878e39SDag-Erling Smørgrav /* _fetch_connect() has already set an error code */ 700*e4878e39SDag-Erling Smørgrav return -1; 701*e4878e39SDag-Erling Smørgrav *proxy = 0; 702*e4878e39SDag-Erling Smørgrav } 703*e4878e39SDag-Erling Smørgrav 704*e4878e39SDag-Erling Smørgrav return fd; 705*e4878e39SDag-Erling Smørgrav } 706*e4878e39SDag-Erling Smørgrav 707*e4878e39SDag-Erling Smørgrav 708*e4878e39SDag-Erling Smørgrav /***************************************************************************** 709*e4878e39SDag-Erling Smørgrav * Core 710*e4878e39SDag-Erling Smørgrav */ 711*e4878e39SDag-Erling Smørgrav 712*e4878e39SDag-Erling Smørgrav /* 713*e4878e39SDag-Erling Smørgrav * Send a request and process the reply 714*e4878e39SDag-Erling Smørgrav */ 715*e4878e39SDag-Erling Smørgrav static FILE * 716*e4878e39SDag-Erling Smørgrav _http_request(struct url *URL, char *op, struct url_stat *us, char *flags) 717*e4878e39SDag-Erling Smørgrav { 718*e4878e39SDag-Erling Smørgrav struct url *url, *new; 719*e4878e39SDag-Erling Smørgrav int chunked, need_auth, noredirect, proxy, verbose; 720*e4878e39SDag-Erling Smørgrav int code, fd, i, n; 721*e4878e39SDag-Erling Smørgrav off_t offset; 722*e4878e39SDag-Erling Smørgrav char *p; 723*e4878e39SDag-Erling Smørgrav FILE *f; 724*e4878e39SDag-Erling Smørgrav hdr h; 72528c645cfSHajimu UMEMOTO char *host; 72628c645cfSHajimu UMEMOTO #ifdef INET6 72728c645cfSHajimu UMEMOTO char hbuf[MAXHOSTNAMELEN + 1]; 72828c645cfSHajimu UMEMOTO #endif 72960245e42SDag-Erling Smørgrav 730*e4878e39SDag-Erling Smørgrav noredirect = (flags && strchr(flags, 'A')); 73160245e42SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 7324ca1ab94SDag-Erling Smørgrav 733*e4878e39SDag-Erling Smørgrav n = noredirect ? 1 : MAX_REDIRECT; 734*e4878e39SDag-Erling Smørgrav 735*e4878e39SDag-Erling Smørgrav us->size = -1; 736*e4878e39SDag-Erling Smørgrav us->atime = us->mtime = 0; 737*e4878e39SDag-Erling Smørgrav chunked = 0; 738*e4878e39SDag-Erling Smørgrav offset = 0; 739*e4878e39SDag-Erling Smørgrav fd = -1; 740*e4878e39SDag-Erling Smørgrav code = HTTP_PROTOCOL_ERROR; /* just to appease a compiler warning */ 741*e4878e39SDag-Erling Smørgrav 742*e4878e39SDag-Erling Smørgrav for (url = URL, i = 0; i < n; ++i) { 743*e4878e39SDag-Erling Smørgrav need_auth = 0; 744*e4878e39SDag-Erling Smørgrav retry: 745*e4878e39SDag-Erling Smørgrav /* connect to server or proxy */ 746*e4878e39SDag-Erling Smørgrav if ((fd = _http_connect(url, &proxy, flags)) == -1) 747*e4878e39SDag-Erling Smørgrav goto ouch; 748*e4878e39SDag-Erling Smørgrav 749*e4878e39SDag-Erling Smørgrav host = url->host; 75028c645cfSHajimu UMEMOTO #ifdef INET6 751*e4878e39SDag-Erling Smørgrav if (strchr(url->host, ':')) { 752*e4878e39SDag-Erling Smørgrav snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); 75328c645cfSHajimu UMEMOTO host = hbuf; 75428c645cfSHajimu UMEMOTO } 75528c645cfSHajimu UMEMOTO #endif 75628c645cfSHajimu UMEMOTO 757*e4878e39SDag-Erling Smørgrav /* send request */ 7580fba3a00SDag-Erling Smørgrav if (verbose) 759c97925adSHajimu UMEMOTO _fetch_info("requesting %s://%s:%d%s", 760*e4878e39SDag-Erling Smørgrav url->scheme, host, url->port, url->doc); 761*e4878e39SDag-Erling Smørgrav if (proxy) { 762*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "%s %s://%s:%d%s HTTP/1.1", 763*e4878e39SDag-Erling Smørgrav op, url->scheme, host, url->port, url->doc); 764*e4878e39SDag-Erling Smørgrav } else { 765*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "%s %s HTTP/1.1", 766*e4878e39SDag-Erling Smørgrav op, url->doc); 76760245e42SDag-Erling Smørgrav } 76860245e42SDag-Erling Smørgrav 769*e4878e39SDag-Erling Smørgrav /* proxy authorization */ 770*e4878e39SDag-Erling Smørgrav if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL) 771*e4878e39SDag-Erling Smørgrav _http_authorize(fd, "Proxy-Authorization", p); 772*e4878e39SDag-Erling Smørgrav 773*e4878e39SDag-Erling Smørgrav /* server authorization */ 774*e4878e39SDag-Erling Smørgrav if (need_auth) { 775*e4878e39SDag-Erling Smørgrav if (*url->user || *url->pwd) 776*e4878e39SDag-Erling Smørgrav _http_basic_auth(fd, "Authorization", 777*e4878e39SDag-Erling Smørgrav url->user ? url->user : "", 778*e4878e39SDag-Erling Smørgrav url->pwd ? url->pwd : ""); 779*e4878e39SDag-Erling Smørgrav else if ((p = getenv("HTTP_AUTH")) != NULL) 780*e4878e39SDag-Erling Smørgrav _http_authorize(fd, "Authorization", p); 781*e4878e39SDag-Erling Smørgrav else { 782*e4878e39SDag-Erling Smørgrav _http_seterr(HTTP_NEED_AUTH); 783*e4878e39SDag-Erling Smørgrav goto ouch; 784*e4878e39SDag-Erling Smørgrav } 785*e4878e39SDag-Erling Smørgrav } 786*e4878e39SDag-Erling Smørgrav 787*e4878e39SDag-Erling Smørgrav /* other headers */ 788*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "Host: %s:%d", host, url->port); 789*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "User-Agent: %s " _LIBFETCH_VER, __progname); 790*e4878e39SDag-Erling Smørgrav if (URL->offset) 791*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "Range: bytes=%lld-", url->offset); 792*e4878e39SDag-Erling Smørgrav _http_cmd(fd, "Connection: close"); 793*e4878e39SDag-Erling Smørgrav _http_cmd(fd, ""); 794*e4878e39SDag-Erling Smørgrav 795*e4878e39SDag-Erling Smørgrav /* get reply */ 796*e4878e39SDag-Erling Smørgrav switch ((code = _http_get_reply(fd))) { 797*e4878e39SDag-Erling Smørgrav case HTTP_OK: 798*e4878e39SDag-Erling Smørgrav case HTTP_PARTIAL: 799*e4878e39SDag-Erling Smørgrav /* fine */ 800*e4878e39SDag-Erling Smørgrav break; 801*e4878e39SDag-Erling Smørgrav case HTTP_MOVED_PERM: 802*e4878e39SDag-Erling Smørgrav case HTTP_MOVED_TEMP: 803*e4878e39SDag-Erling Smørgrav /* 804*e4878e39SDag-Erling Smørgrav * Not so fine, but we still have to read the headers to 805*e4878e39SDag-Erling Smørgrav * get the new location. 806*e4878e39SDag-Erling Smørgrav */ 807*e4878e39SDag-Erling Smørgrav break; 808*e4878e39SDag-Erling Smørgrav case HTTP_NEED_AUTH: 809*e4878e39SDag-Erling Smørgrav if (need_auth) { 810*e4878e39SDag-Erling Smørgrav /* 811*e4878e39SDag-Erling Smørgrav * We already sent out authorization code, so there's 812*e4878e39SDag-Erling Smørgrav * nothing more we can do. 813*e4878e39SDag-Erling Smørgrav */ 814*e4878e39SDag-Erling Smørgrav _http_seterr(code); 815*e4878e39SDag-Erling Smørgrav goto ouch; 816*e4878e39SDag-Erling Smørgrav } 817*e4878e39SDag-Erling Smørgrav /* try again, but send the password this time */ 818*e4878e39SDag-Erling Smørgrav if (verbose) 819*e4878e39SDag-Erling Smørgrav _fetch_info("server requires authorization"); 820*e4878e39SDag-Erling Smørgrav need_auth = 1; 821*e4878e39SDag-Erling Smørgrav close(fd); 822*e4878e39SDag-Erling Smørgrav goto retry; 823*e4878e39SDag-Erling Smørgrav case HTTP_NEED_PROXY_AUTH: 824*e4878e39SDag-Erling Smørgrav /* 825*e4878e39SDag-Erling Smørgrav * If we're talking to a proxy, we already sent our proxy 826*e4878e39SDag-Erling Smørgrav * authorization code, so there's nothing more we can do. 827*e4878e39SDag-Erling Smørgrav */ 828*e4878e39SDag-Erling Smørgrav _http_seterr(code); 829*e4878e39SDag-Erling Smørgrav goto ouch; 830*e4878e39SDag-Erling Smørgrav case HTTP_PROTOCOL_ERROR: 831*e4878e39SDag-Erling Smørgrav /* fall through */ 832*e4878e39SDag-Erling Smørgrav case -1: 833*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 834*e4878e39SDag-Erling Smørgrav goto ouch; 835*e4878e39SDag-Erling Smørgrav default: 836*e4878e39SDag-Erling Smørgrav _http_seterr(code); 837*e4878e39SDag-Erling Smørgrav goto ouch; 838*e4878e39SDag-Erling Smørgrav } 839*e4878e39SDag-Erling Smørgrav 840*e4878e39SDag-Erling Smørgrav /* get headers */ 841*e4878e39SDag-Erling Smørgrav do { 842*e4878e39SDag-Erling Smørgrav switch ((h = _http_next_header(fd, &p))) { 843*e4878e39SDag-Erling Smørgrav case hdr_syserror: 844*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 845*e4878e39SDag-Erling Smørgrav goto ouch; 846*e4878e39SDag-Erling Smørgrav case hdr_error: 847*e4878e39SDag-Erling Smørgrav _http_seterr(HTTP_PROTOCOL_ERROR); 848*e4878e39SDag-Erling Smørgrav goto ouch; 849*e4878e39SDag-Erling Smørgrav case hdr_content_length: 850*e4878e39SDag-Erling Smørgrav us->size = _http_parse_length(p); 851*e4878e39SDag-Erling Smørgrav break; 852*e4878e39SDag-Erling Smørgrav case hdr_content_range: 853*e4878e39SDag-Erling Smørgrav offset = _http_parse_range(p); 854*e4878e39SDag-Erling Smørgrav break; 855*e4878e39SDag-Erling Smørgrav case hdr_last_modified: 856*e4878e39SDag-Erling Smørgrav us->atime = us->mtime = _http_parse_mtime(p); 857*e4878e39SDag-Erling Smørgrav break; 858*e4878e39SDag-Erling Smørgrav case hdr_location: 859*e4878e39SDag-Erling Smørgrav if (!HTTP_REDIRECT(code)) 860*e4878e39SDag-Erling Smørgrav break; 861*e4878e39SDag-Erling Smørgrav if (verbose) 862*e4878e39SDag-Erling Smørgrav _fetch_info("%d redirect to %s", code, p); 863*e4878e39SDag-Erling Smørgrav if ((new = fetchParseURL(p)) == NULL) 864*e4878e39SDag-Erling Smørgrav /* invalid location */ 865*e4878e39SDag-Erling Smørgrav goto ouch; 866*e4878e39SDag-Erling Smørgrav if (!*new->user && !*new->pwd) { 867*e4878e39SDag-Erling Smørgrav strcpy(new->user, url->user); 868*e4878e39SDag-Erling Smørgrav strcpy(new->pwd, url->pwd); 869*e4878e39SDag-Erling Smørgrav } 870*e4878e39SDag-Erling Smørgrav new->offset = url->offset; 871*e4878e39SDag-Erling Smørgrav new->length = url->length; 872*e4878e39SDag-Erling Smørgrav close(fd); 873*e4878e39SDag-Erling Smørgrav us->size = -1; 874*e4878e39SDag-Erling Smørgrav us->atime = us->mtime = 0; 875*e4878e39SDag-Erling Smørgrav chunked = 0; 876*e4878e39SDag-Erling Smørgrav offset = 0; 877*e4878e39SDag-Erling Smørgrav fd = -1; 878*e4878e39SDag-Erling Smørgrav if (url != URL) 879*e4878e39SDag-Erling Smørgrav fetchFreeURL(url); 880*e4878e39SDag-Erling Smørgrav url = new; 881*e4878e39SDag-Erling Smørgrav continue; 882*e4878e39SDag-Erling Smørgrav case hdr_transfer_encoding: 883*e4878e39SDag-Erling Smørgrav /* XXX weak test*/ 884*e4878e39SDag-Erling Smørgrav chunked = (strcasecmp(p, "chunked") == 0); 885*e4878e39SDag-Erling Smørgrav break; 886*e4878e39SDag-Erling Smørgrav case hdr_end: 887*e4878e39SDag-Erling Smørgrav /* fall through */ 888*e4878e39SDag-Erling Smørgrav case hdr_unknown: 889*e4878e39SDag-Erling Smørgrav /* ignore */ 890*e4878e39SDag-Erling Smørgrav break; 891*e4878e39SDag-Erling Smørgrav } 892*e4878e39SDag-Erling Smørgrav } while (h > hdr_end); 893*e4878e39SDag-Erling Smørgrav 894*e4878e39SDag-Erling Smørgrav if (code == HTTP_OK || code == HTTP_PARTIAL) 895*e4878e39SDag-Erling Smørgrav break; 896*e4878e39SDag-Erling Smørgrav } 897*e4878e39SDag-Erling Smørgrav 898*e4878e39SDag-Erling Smørgrav /* no success */ 899*e4878e39SDag-Erling Smørgrav if (fd == -1) { 900*e4878e39SDag-Erling Smørgrav _http_seterr(code); 901*e4878e39SDag-Erling Smørgrav goto ouch; 902*e4878e39SDag-Erling Smørgrav } 903*e4878e39SDag-Erling Smørgrav 904*e4878e39SDag-Erling Smørgrav /* wrap it up in a FILE */ 905*e4878e39SDag-Erling Smørgrav if ((f = chunked ? _http_funopen(fd) : fdopen(fd, "r")) == NULL) { 906*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 907*e4878e39SDag-Erling Smørgrav goto ouch; 908*e4878e39SDag-Erling Smørgrav } 909*e4878e39SDag-Erling Smørgrav 910*e4878e39SDag-Erling Smørgrav while (offset++ < url->offset) 911*e4878e39SDag-Erling Smørgrav if (fgetc(f) == EOF) { 912*e4878e39SDag-Erling Smørgrav _fetch_syserr(); 913*e4878e39SDag-Erling Smørgrav fclose(f); 914*e4878e39SDag-Erling Smørgrav f = NULL; 915*e4878e39SDag-Erling Smørgrav } 916*e4878e39SDag-Erling Smørgrav 917*e4878e39SDag-Erling Smørgrav if (url != URL) 918*e4878e39SDag-Erling Smørgrav fetchFreeURL(url); 919*e4878e39SDag-Erling Smørgrav 920*e4878e39SDag-Erling Smørgrav return f; 921*e4878e39SDag-Erling Smørgrav 922*e4878e39SDag-Erling Smørgrav ouch: 923*e4878e39SDag-Erling Smørgrav if (url != URL) 924*e4878e39SDag-Erling Smørgrav fetchFreeURL(url); 925*e4878e39SDag-Erling Smørgrav if (fd != -1) 926*e4878e39SDag-Erling Smørgrav close(fd); 927*e4878e39SDag-Erling Smørgrav return NULL; 928*e4878e39SDag-Erling Smørgrav } 929*e4878e39SDag-Erling Smørgrav 930*e4878e39SDag-Erling Smørgrav 931*e4878e39SDag-Erling Smørgrav /***************************************************************************** 932*e4878e39SDag-Erling Smørgrav * Entry points 933*e4878e39SDag-Erling Smørgrav */ 934*e4878e39SDag-Erling Smørgrav 93560245e42SDag-Erling Smørgrav /* 93660245e42SDag-Erling Smørgrav * Retrieve a file by HTTP 93760245e42SDag-Erling Smørgrav */ 93860245e42SDag-Erling Smørgrav FILE * 93960245e42SDag-Erling Smørgrav fetchGetHTTP(struct url *URL, char *flags) 94060245e42SDag-Erling Smørgrav { 941*e4878e39SDag-Erling Smørgrav struct url_stat us; 94260245e42SDag-Erling Smørgrav 943*e4878e39SDag-Erling Smørgrav return _http_request(URL, "GET", &us, flags); 9444ca1ab94SDag-Erling Smørgrav } 9454ca1ab94SDag-Erling Smørgrav 9464ca1ab94SDag-Erling Smørgrav FILE * 947d8acd8dcSDag-Erling Smørgrav fetchPutHTTP(struct url *URL, char *flags) 9484ca1ab94SDag-Erling Smørgrav { 9494ca1ab94SDag-Erling Smørgrav warnx("fetchPutHTTP(): not implemented"); 9504ca1ab94SDag-Erling Smørgrav return NULL; 9514ca1ab94SDag-Erling Smørgrav } 952d8acd8dcSDag-Erling Smørgrav 953d8acd8dcSDag-Erling Smørgrav /* 954d8acd8dcSDag-Erling Smørgrav * Get an HTTP document's metadata 955d8acd8dcSDag-Erling Smørgrav */ 956d8acd8dcSDag-Erling Smørgrav int 95760245e42SDag-Erling Smørgrav fetchStatHTTP(struct url *URL, struct url_stat *us, char *flags) 958d8acd8dcSDag-Erling Smørgrav { 95960245e42SDag-Erling Smørgrav FILE *f; 96060245e42SDag-Erling Smørgrav 961*e4878e39SDag-Erling Smørgrav if ((f = _http_request(URL, "HEAD", us, flags)) == NULL) 96260245e42SDag-Erling Smørgrav return -1; 963f8f4130bSDag-Erling Smørgrav fclose(f); 96460245e42SDag-Erling Smørgrav return 0; 965d8acd8dcSDag-Erling Smørgrav } 966ce71b736SDag-Erling Smørgrav 967ce71b736SDag-Erling Smørgrav /* 968ce71b736SDag-Erling Smørgrav * List a directory 969ce71b736SDag-Erling Smørgrav */ 970ce71b736SDag-Erling Smørgrav struct url_ent * 971ce71b736SDag-Erling Smørgrav fetchListHTTP(struct url *url, char *flags) 972ce71b736SDag-Erling Smørgrav { 973ce71b736SDag-Erling Smørgrav warnx("fetchListHTTP(): not implemented"); 974ce71b736SDag-Erling Smørgrav return NULL; 975ce71b736SDag-Erling Smørgrav } 976