1 1.6 christos /* $NetBSD: http.c,v 1.6 2024/09/01 15:07:31 christos Exp $ */ 2 1.1 joerg /*- 3 1.1 joerg * Copyright (c) 2000-2004 Dag-Erling Codan Smrgrav 4 1.1 joerg * Copyright (c) 2003 Thomas Klausner <wiz (at) NetBSD.org> 5 1.2 christos * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg (at) NetBSD.org> 6 1.1 joerg * All rights reserved. 7 1.1 joerg * 8 1.1 joerg * Redistribution and use in source and binary forms, with or without 9 1.1 joerg * modification, are permitted provided that the following conditions 10 1.1 joerg * are met: 11 1.1 joerg * 1. Redistributions of source code must retain the above copyright 12 1.1 joerg * notice, this list of conditions and the following disclaimer 13 1.1 joerg * in this position and unchanged. 14 1.1 joerg * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 joerg * notice, this list of conditions and the following disclaimer in the 16 1.1 joerg * documentation and/or other materials provided with the distribution. 17 1.1 joerg * 3. The name of the author may not be used to endorse or promote products 18 1.1 joerg * derived from this software without specific prior written permission. 19 1.1 joerg * 20 1.1 joerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 1.1 joerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 1.1 joerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 1.1 joerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 1.1 joerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 1.1 joerg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 1.1 joerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 1.1 joerg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 1.1 joerg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 1.1 joerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 1.1 joerg * 31 1.1 joerg * $FreeBSD: http.c,v 1.83 2008/02/06 11:39:55 des Exp $ 32 1.1 joerg */ 33 1.1 joerg 34 1.1 joerg /* 35 1.1 joerg * The following copyright applies to the base64 code: 36 1.1 joerg * 37 1.1 joerg *- 38 1.1 joerg * Copyright 1997 Massachusetts Institute of Technology 39 1.1 joerg * 40 1.1 joerg * Permission to use, copy, modify, and distribute this software and 41 1.1 joerg * its documentation for any purpose and without fee is hereby 42 1.1 joerg * granted, provided that both the above copyright notice and this 43 1.1 joerg * permission notice appear in all copies, that both the above 44 1.1 joerg * copyright notice and this permission notice appear in all 45 1.1 joerg * supporting documentation, and that the name of M.I.T. not be used 46 1.1 joerg * in advertising or publicity pertaining to distribution of the 47 1.1 joerg * software without specific, written prior permission. M.I.T. makes 48 1.1 joerg * no representations about the suitability of this software for any 49 1.1 joerg * purpose. It is provided "as is" without express or implied 50 1.1 joerg * warranty. 51 1.1 joerg * 52 1.1 joerg * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 53 1.1 joerg * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 54 1.1 joerg * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 55 1.1 joerg * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 56 1.1 joerg * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 57 1.1 joerg * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 58 1.1 joerg * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 59 1.1 joerg * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 60 1.1 joerg * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 61 1.1 joerg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 62 1.1 joerg * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 1.1 joerg * SUCH DAMAGE. 64 1.1 joerg */ 65 1.1 joerg 66 1.5 christos #if defined(__linux__) || defined(__MINT__) || defined(__FreeBSD_kernel__) 67 1.2 christos /* Keep this down to Linux or MiNT, it can create surprises elsewhere. */ 68 1.5 christos /* 69 1.5 christos __FreeBSD_kernel__ is defined for GNU/kFreeBSD. 70 1.5 christos See http://glibc-bsd.alioth.debian.org/porting/PORTING . 71 1.5 christos */ 72 1.1 joerg #define _GNU_SOURCE 73 1.1 joerg #endif 74 1.1 joerg 75 1.4 kamil #ifndef _REENTRANT 76 1.2 christos /* Needed for gmtime_r on Interix */ 77 1.2 christos #define _REENTRANT 78 1.4 kamil #endif 79 1.2 christos 80 1.1 joerg #if HAVE_CONFIG_H 81 1.1 joerg #include "config.h" 82 1.1 joerg #endif 83 1.1 joerg #ifndef NETBSD 84 1.1 joerg #include <nbcompat.h> 85 1.1 joerg #endif 86 1.1 joerg 87 1.1 joerg #include <sys/types.h> 88 1.1 joerg #include <sys/socket.h> 89 1.1 joerg 90 1.1 joerg #include <ctype.h> 91 1.1 joerg #include <errno.h> 92 1.1 joerg #include <locale.h> 93 1.1 joerg #include <stdarg.h> 94 1.1 joerg #ifndef NETBSD 95 1.1 joerg #include <nbcompat/stdio.h> 96 1.1 joerg #else 97 1.1 joerg #include <stdio.h> 98 1.1 joerg #endif 99 1.1 joerg #include <stdlib.h> 100 1.1 joerg #include <string.h> 101 1.1 joerg #include <time.h> 102 1.1 joerg #include <unistd.h> 103 1.1 joerg 104 1.1 joerg #include <netinet/in.h> 105 1.1 joerg #include <netinet/tcp.h> 106 1.1 joerg 107 1.2 christos #ifndef NETBSD 108 1.2 christos #include <nbcompat/netdb.h> 109 1.2 christos #else 110 1.2 christos #include <netdb.h> 111 1.2 christos #endif 112 1.2 christos 113 1.2 christos #include <arpa/inet.h> 114 1.2 christos 115 1.1 joerg #include "fetch.h" 116 1.1 joerg #include "common.h" 117 1.1 joerg #include "httperr.h" 118 1.1 joerg 119 1.1 joerg /* Maximum number of redirects to follow */ 120 1.1 joerg #define MAX_REDIRECT 5 121 1.1 joerg 122 1.1 joerg /* Symbolic names for reply codes we care about */ 123 1.1 joerg #define HTTP_OK 200 124 1.1 joerg #define HTTP_PARTIAL 206 125 1.1 joerg #define HTTP_MOVED_PERM 301 126 1.1 joerg #define HTTP_MOVED_TEMP 302 127 1.1 joerg #define HTTP_SEE_OTHER 303 128 1.2 christos #define HTTP_NOT_MODIFIED 304 129 1.1 joerg #define HTTP_TEMP_REDIRECT 307 130 1.1 joerg #define HTTP_NEED_AUTH 401 131 1.1 joerg #define HTTP_NEED_PROXY_AUTH 407 132 1.1 joerg #define HTTP_BAD_RANGE 416 133 1.1 joerg #define HTTP_PROTOCOL_ERROR 999 134 1.1 joerg 135 1.1 joerg #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ 136 1.1 joerg || (xyz) == HTTP_MOVED_TEMP \ 137 1.1 joerg || (xyz) == HTTP_TEMP_REDIRECT \ 138 1.1 joerg || (xyz) == HTTP_SEE_OTHER) 139 1.1 joerg 140 1.1 joerg #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) 141 1.1 joerg 142 1.1 joerg 143 1.1 joerg /***************************************************************************** 144 1.1 joerg * I/O functions for decoding chunked streams 145 1.1 joerg */ 146 1.1 joerg 147 1.1 joerg struct httpio 148 1.1 joerg { 149 1.1 joerg conn_t *conn; /* connection */ 150 1.1 joerg int chunked; /* chunked mode */ 151 1.2 christos int keep_alive; /* keep-alive mode */ 152 1.1 joerg char *buf; /* chunk buffer */ 153 1.1 joerg size_t bufsize; /* size of chunk buffer */ 154 1.1 joerg ssize_t buflen; /* amount of data currently in buffer */ 155 1.2 christos size_t bufpos; /* current read offset in buffer */ 156 1.1 joerg int eof; /* end-of-file flag */ 157 1.1 joerg int error; /* error flag */ 158 1.1 joerg size_t chunksize; /* remaining size of current chunk */ 159 1.2 christos off_t contentlength; /* remaining size of the content */ 160 1.1 joerg }; 161 1.1 joerg 162 1.1 joerg /* 163 1.1 joerg * Get next chunk header 164 1.1 joerg */ 165 1.2 christos static ssize_t 166 1.1 joerg http_new_chunk(struct httpio *io) 167 1.1 joerg { 168 1.1 joerg char *p; 169 1.1 joerg 170 1.1 joerg if (fetch_getln(io->conn) == -1) 171 1.1 joerg return (-1); 172 1.1 joerg 173 1.1 joerg if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf)) 174 1.1 joerg return (-1); 175 1.1 joerg 176 1.1 joerg for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) { 177 1.1 joerg if (*p == ';') 178 1.1 joerg break; 179 1.1 joerg if (!isxdigit((unsigned char)*p)) 180 1.1 joerg return (-1); 181 1.1 joerg if (isdigit((unsigned char)*p)) { 182 1.1 joerg io->chunksize = io->chunksize * 16 + 183 1.1 joerg *p - '0'; 184 1.1 joerg } else { 185 1.1 joerg io->chunksize = io->chunksize * 16 + 186 1.1 joerg 10 + tolower((unsigned char)*p) - 'a'; 187 1.1 joerg } 188 1.1 joerg } 189 1.1 joerg 190 1.1 joerg return (io->chunksize); 191 1.1 joerg } 192 1.1 joerg 193 1.1 joerg /* 194 1.1 joerg * Grow the input buffer to at least len bytes 195 1.1 joerg */ 196 1.1 joerg static int 197 1.1 joerg http_growbuf(struct httpio *io, size_t len) 198 1.1 joerg { 199 1.1 joerg char *tmp; 200 1.1 joerg 201 1.1 joerg if (io->bufsize >= len) 202 1.1 joerg return (0); 203 1.1 joerg 204 1.1 joerg if ((tmp = realloc(io->buf, len)) == NULL) 205 1.1 joerg return (-1); 206 1.1 joerg io->buf = tmp; 207 1.1 joerg io->bufsize = len; 208 1.1 joerg return (0); 209 1.1 joerg } 210 1.1 joerg 211 1.1 joerg /* 212 1.1 joerg * Fill the input buffer, do chunk decoding on the fly 213 1.1 joerg */ 214 1.2 christos static ssize_t 215 1.1 joerg http_fillbuf(struct httpio *io, size_t len) 216 1.1 joerg { 217 1.1 joerg if (io->error) 218 1.1 joerg return (-1); 219 1.1 joerg if (io->eof) 220 1.1 joerg return (0); 221 1.1 joerg 222 1.2 christos if (io->contentlength >= 0 && (off_t)len > io->contentlength) 223 1.2 christos len = io->contentlength; 224 1.2 christos 225 1.1 joerg if (io->chunked == 0) { 226 1.1 joerg if (http_growbuf(io, len) == -1) 227 1.1 joerg return (-1); 228 1.1 joerg if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { 229 1.1 joerg io->error = 1; 230 1.1 joerg return (-1); 231 1.1 joerg } 232 1.2 christos if (io->contentlength) 233 1.2 christos io->contentlength -= io->buflen; 234 1.1 joerg io->bufpos = 0; 235 1.1 joerg return (io->buflen); 236 1.1 joerg } 237 1.1 joerg 238 1.1 joerg if (io->chunksize == 0) { 239 1.1 joerg switch (http_new_chunk(io)) { 240 1.1 joerg case -1: 241 1.1 joerg io->error = 1; 242 1.1 joerg return (-1); 243 1.1 joerg case 0: 244 1.1 joerg io->eof = 1; 245 1.2 christos if (fetch_getln(io->conn) == -1) 246 1.2 christos return (-1); 247 1.1 joerg return (0); 248 1.1 joerg } 249 1.1 joerg } 250 1.1 joerg 251 1.1 joerg if (len > io->chunksize) 252 1.1 joerg len = io->chunksize; 253 1.1 joerg if (http_growbuf(io, len) == -1) 254 1.1 joerg return (-1); 255 1.1 joerg if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { 256 1.1 joerg io->error = 1; 257 1.1 joerg return (-1); 258 1.1 joerg } 259 1.1 joerg io->chunksize -= io->buflen; 260 1.2 christos if (io->contentlength >= 0) 261 1.2 christos io->contentlength -= io->buflen; 262 1.1 joerg 263 1.1 joerg if (io->chunksize == 0) { 264 1.1 joerg char endl[2]; 265 1.1 joerg ssize_t len2; 266 1.1 joerg 267 1.1 joerg len2 = fetch_read(io->conn, endl, 2); 268 1.1 joerg if (len2 == 1 && fetch_read(io->conn, endl + 1, 1) != 1) 269 1.1 joerg return (-1); 270 1.1 joerg if (len2 == -1 || endl[0] != '\r' || endl[1] != '\n') 271 1.1 joerg return (-1); 272 1.1 joerg } 273 1.1 joerg 274 1.1 joerg io->bufpos = 0; 275 1.1 joerg 276 1.1 joerg return (io->buflen); 277 1.1 joerg } 278 1.1 joerg 279 1.1 joerg /* 280 1.1 joerg * Read function 281 1.1 joerg */ 282 1.1 joerg static ssize_t 283 1.1 joerg http_readfn(void *v, void *buf, size_t len) 284 1.1 joerg { 285 1.1 joerg struct httpio *io = (struct httpio *)v; 286 1.1 joerg size_t l, pos; 287 1.1 joerg 288 1.1 joerg if (io->error) 289 1.1 joerg return (-1); 290 1.1 joerg if (io->eof) 291 1.1 joerg return (0); 292 1.1 joerg 293 1.1 joerg for (pos = 0; len > 0; pos += l, len -= l) { 294 1.1 joerg /* empty buffer */ 295 1.2 christos if (!io->buf || (ssize_t)io->bufpos == io->buflen) 296 1.1 joerg if (http_fillbuf(io, len) < 1) 297 1.1 joerg break; 298 1.1 joerg l = io->buflen - io->bufpos; 299 1.1 joerg if (len < l) 300 1.1 joerg l = len; 301 1.1 joerg memcpy((char *)buf + pos, io->buf + io->bufpos, l); 302 1.1 joerg io->bufpos += l; 303 1.1 joerg } 304 1.1 joerg 305 1.1 joerg if (!pos && io->error) 306 1.1 joerg return (-1); 307 1.1 joerg return (pos); 308 1.1 joerg } 309 1.1 joerg 310 1.1 joerg /* 311 1.1 joerg * Write function 312 1.1 joerg */ 313 1.1 joerg static ssize_t 314 1.1 joerg http_writefn(void *v, const void *buf, size_t len) 315 1.1 joerg { 316 1.1 joerg struct httpio *io = (struct httpio *)v; 317 1.1 joerg 318 1.1 joerg return (fetch_write(io->conn, buf, len)); 319 1.1 joerg } 320 1.1 joerg 321 1.1 joerg /* 322 1.1 joerg * Close function 323 1.1 joerg */ 324 1.1 joerg static void 325 1.1 joerg http_closefn(void *v) 326 1.1 joerg { 327 1.1 joerg struct httpio *io = (struct httpio *)v; 328 1.1 joerg 329 1.2 christos if (io->keep_alive) { 330 1.2 christos int val; 331 1.2 christos 332 1.2 christos val = 0; 333 1.2 christos setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, 334 1.2 christos (socklen_t)sizeof(val)); 335 1.2 christos fetch_cache_put(io->conn, fetch_close); 336 1.5 christos #if defined(TCP_NOPUSH) && !defined(__APPLE__) 337 1.2 christos val = 1; 338 1.2 christos setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, 339 1.2 christos sizeof(val)); 340 1.2 christos #endif 341 1.2 christos } else { 342 1.2 christos fetch_close(io->conn); 343 1.2 christos } 344 1.2 christos 345 1.2 christos free(io->buf); 346 1.1 joerg free(io); 347 1.1 joerg } 348 1.1 joerg 349 1.1 joerg /* 350 1.1 joerg * Wrap a file descriptor up 351 1.1 joerg */ 352 1.1 joerg static fetchIO * 353 1.2 christos http_funopen(conn_t *conn, int chunked, int keep_alive, off_t clength) 354 1.1 joerg { 355 1.1 joerg struct httpio *io; 356 1.1 joerg fetchIO *f; 357 1.1 joerg 358 1.1 joerg if ((io = calloc(1, sizeof(*io))) == NULL) { 359 1.1 joerg fetch_syserr(); 360 1.1 joerg return (NULL); 361 1.1 joerg } 362 1.1 joerg io->conn = conn; 363 1.1 joerg io->chunked = chunked; 364 1.2 christos io->contentlength = clength; 365 1.2 christos io->keep_alive = keep_alive; 366 1.1 joerg f = fetchIO_unopen(io, http_readfn, http_writefn, http_closefn); 367 1.1 joerg if (f == NULL) { 368 1.1 joerg fetch_syserr(); 369 1.1 joerg free(io); 370 1.1 joerg return (NULL); 371 1.1 joerg } 372 1.1 joerg return (f); 373 1.1 joerg } 374 1.1 joerg 375 1.1 joerg 376 1.1 joerg /***************************************************************************** 377 1.1 joerg * Helper functions for talking to the server and parsing its replies 378 1.1 joerg */ 379 1.1 joerg 380 1.1 joerg /* Header types */ 381 1.1 joerg typedef enum { 382 1.1 joerg hdr_syserror = -2, 383 1.1 joerg hdr_error = -1, 384 1.1 joerg hdr_end = 0, 385 1.1 joerg hdr_unknown = 1, 386 1.2 christos hdr_connection, 387 1.1 joerg hdr_content_length, 388 1.1 joerg hdr_content_range, 389 1.1 joerg hdr_last_modified, 390 1.1 joerg hdr_location, 391 1.1 joerg hdr_transfer_encoding, 392 1.1 joerg hdr_www_authenticate 393 1.1 joerg } hdr_t; 394 1.1 joerg 395 1.1 joerg /* Names of interesting headers */ 396 1.1 joerg static struct { 397 1.1 joerg hdr_t num; 398 1.1 joerg const char *name; 399 1.1 joerg } hdr_names[] = { 400 1.2 christos { hdr_connection, "Connection" }, 401 1.1 joerg { hdr_content_length, "Content-Length" }, 402 1.1 joerg { hdr_content_range, "Content-Range" }, 403 1.1 joerg { hdr_last_modified, "Last-Modified" }, 404 1.1 joerg { hdr_location, "Location" }, 405 1.1 joerg { hdr_transfer_encoding, "Transfer-Encoding" }, 406 1.1 joerg { hdr_www_authenticate, "WWW-Authenticate" }, 407 1.1 joerg { hdr_unknown, NULL }, 408 1.1 joerg }; 409 1.1 joerg 410 1.1 joerg /* 411 1.1 joerg * Send a formatted line; optionally echo to terminal 412 1.1 joerg */ 413 1.5 christos LIBFETCH_PRINTFLIKE(2, 3) 414 1.1 joerg static int 415 1.1 joerg http_cmd(conn_t *conn, const char *fmt, ...) 416 1.1 joerg { 417 1.1 joerg va_list ap; 418 1.1 joerg size_t len; 419 1.1 joerg char *msg; 420 1.2 christos ssize_t r; 421 1.1 joerg 422 1.1 joerg va_start(ap, fmt); 423 1.1 joerg len = vasprintf(&msg, fmt, ap); 424 1.1 joerg va_end(ap); 425 1.1 joerg 426 1.1 joerg if (msg == NULL) { 427 1.1 joerg errno = ENOMEM; 428 1.1 joerg fetch_syserr(); 429 1.1 joerg return (-1); 430 1.1 joerg } 431 1.1 joerg 432 1.2 christos r = fetch_write(conn, msg, len); 433 1.1 joerg free(msg); 434 1.1 joerg 435 1.1 joerg if (r == -1) { 436 1.1 joerg fetch_syserr(); 437 1.1 joerg return (-1); 438 1.1 joerg } 439 1.1 joerg 440 1.1 joerg return (0); 441 1.1 joerg } 442 1.1 joerg 443 1.1 joerg /* 444 1.1 joerg * Get and parse status line 445 1.1 joerg */ 446 1.1 joerg static int 447 1.1 joerg http_get_reply(conn_t *conn) 448 1.1 joerg { 449 1.1 joerg char *p; 450 1.1 joerg 451 1.1 joerg if (fetch_getln(conn) == -1) 452 1.1 joerg return (-1); 453 1.1 joerg /* 454 1.1 joerg * A valid status line looks like "HTTP/m.n xyz reason" where m 455 1.1 joerg * and n are the major and minor protocol version numbers and xyz 456 1.1 joerg * is the reply code. 457 1.1 joerg * Unfortunately, there are servers out there (NCSA 1.5.1, to name 458 1.1 joerg * just one) that do not send a version number, so we can't rely 459 1.1 joerg * on finding one, but if we do, insist on it being 1.0 or 1.1. 460 1.1 joerg * We don't care about the reason phrase. 461 1.1 joerg */ 462 1.1 joerg if (strncmp(conn->buf, "HTTP", 4) != 0) 463 1.1 joerg return (HTTP_PROTOCOL_ERROR); 464 1.1 joerg p = conn->buf + 4; 465 1.1 joerg if (*p == '/') { 466 1.1 joerg if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1')) 467 1.1 joerg return (HTTP_PROTOCOL_ERROR); 468 1.1 joerg p += 4; 469 1.1 joerg } 470 1.1 joerg if (*p != ' ' || 471 1.1 joerg !isdigit((unsigned char)p[1]) || 472 1.1 joerg !isdigit((unsigned char)p[2]) || 473 1.1 joerg !isdigit((unsigned char)p[3])) 474 1.1 joerg return (HTTP_PROTOCOL_ERROR); 475 1.1 joerg 476 1.1 joerg conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0'); 477 1.1 joerg return (conn->err); 478 1.1 joerg } 479 1.1 joerg 480 1.1 joerg /* 481 1.1 joerg * Check a header; if the type matches the given string, return a pointer 482 1.1 joerg * to the beginning of the value. 483 1.1 joerg */ 484 1.1 joerg static const char * 485 1.1 joerg http_match(const char *str, const char *hdr) 486 1.1 joerg { 487 1.1 joerg while (*str && *hdr && 488 1.1 joerg tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++)) 489 1.1 joerg /* nothing */; 490 1.1 joerg if (*str || *hdr != ':') 491 1.1 joerg return (NULL); 492 1.1 joerg while (*hdr && isspace((unsigned char)*++hdr)) 493 1.1 joerg /* nothing */; 494 1.1 joerg return (hdr); 495 1.1 joerg } 496 1.1 joerg 497 1.1 joerg /* 498 1.1 joerg * Get the next header and return the appropriate symbolic code. 499 1.1 joerg */ 500 1.1 joerg static hdr_t 501 1.1 joerg http_next_header(conn_t *conn, const char **p) 502 1.1 joerg { 503 1.1 joerg int i; 504 1.1 joerg 505 1.1 joerg if (fetch_getln(conn) == -1) 506 1.1 joerg return (hdr_syserror); 507 1.1 joerg while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1])) 508 1.1 joerg conn->buflen--; 509 1.1 joerg conn->buf[conn->buflen] = '\0'; 510 1.1 joerg if (conn->buflen == 0) 511 1.1 joerg return (hdr_end); 512 1.1 joerg /* 513 1.1 joerg * We could check for malformed headers but we don't really care. 514 1.1 joerg * A valid header starts with a token immediately followed by a 515 1.1 joerg * colon; a token is any sequence of non-control, non-whitespace 516 1.1 joerg * characters except "()<>@,;:\\\"{}". 517 1.1 joerg */ 518 1.1 joerg for (i = 0; hdr_names[i].num != hdr_unknown; i++) 519 1.1 joerg if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL) 520 1.1 joerg return (hdr_names[i].num); 521 1.1 joerg return (hdr_unknown); 522 1.1 joerg } 523 1.1 joerg 524 1.1 joerg /* 525 1.1 joerg * Parse a last-modified header 526 1.1 joerg */ 527 1.1 joerg static int 528 1.1 joerg http_parse_mtime(const char *p, time_t *mtime) 529 1.1 joerg { 530 1.1 joerg struct tm tm; 531 1.5 christos char *r; 532 1.5 christos 533 1.5 christos #ifdef LC_C_LOCALE 534 1.5 christos r = strptime_l(p, "%a, %d %b %Y %H:%M:%S GMT", &tm, LC_C_LOCALE); 535 1.5 christos #else 536 1.5 christos char *locale; 537 1.5 christos 538 1.5 christos locale = strdup(setlocale(LC_TIME, NULL)); 539 1.5 christos if (locale == NULL) 540 1.5 christos return (-1); 541 1.1 joerg 542 1.1 joerg setlocale(LC_TIME, "C"); 543 1.1 joerg r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); 544 1.1 joerg /* XXX should add support for date-2 and date-3 */ 545 1.1 joerg setlocale(LC_TIME, locale); 546 1.5 christos free(locale); 547 1.5 christos #endif 548 1.5 christos if (r == NULL) 549 1.5 christos return (-1); 550 1.1 joerg *mtime = timegm(&tm); 551 1.1 joerg return (0); 552 1.1 joerg } 553 1.1 joerg 554 1.1 joerg /* 555 1.1 joerg * Parse a content-length header 556 1.1 joerg */ 557 1.1 joerg static int 558 1.1 joerg http_parse_length(const char *p, off_t *length) 559 1.1 joerg { 560 1.1 joerg off_t len; 561 1.1 joerg 562 1.1 joerg for (len = 0; *p && isdigit((unsigned char)*p); ++p) 563 1.1 joerg len = len * 10 + (*p - '0'); 564 1.1 joerg if (*p) 565 1.1 joerg return (-1); 566 1.1 joerg *length = len; 567 1.1 joerg return (0); 568 1.1 joerg } 569 1.1 joerg 570 1.1 joerg /* 571 1.1 joerg * Parse a content-range header 572 1.1 joerg */ 573 1.1 joerg static int 574 1.1 joerg http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size) 575 1.1 joerg { 576 1.1 joerg off_t first, last, len; 577 1.1 joerg 578 1.1 joerg if (strncasecmp(p, "bytes ", 6) != 0) 579 1.1 joerg return (-1); 580 1.1 joerg p += 6; 581 1.1 joerg if (*p == '*') { 582 1.1 joerg first = last = -1; 583 1.1 joerg ++p; 584 1.1 joerg } else { 585 1.1 joerg for (first = 0; *p && isdigit((unsigned char)*p); ++p) 586 1.1 joerg first = first * 10 + *p - '0'; 587 1.1 joerg if (*p != '-') 588 1.1 joerg return (-1); 589 1.1 joerg for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p) 590 1.1 joerg last = last * 10 + *p - '0'; 591 1.1 joerg } 592 1.1 joerg if (first > last || *p != '/') 593 1.1 joerg return (-1); 594 1.1 joerg for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p) 595 1.1 joerg len = len * 10 + *p - '0'; 596 1.1 joerg if (*p || len < last - first + 1) 597 1.1 joerg return (-1); 598 1.1 joerg if (first == -1) 599 1.1 joerg *length = 0; 600 1.1 joerg else 601 1.1 joerg *length = last - first + 1; 602 1.1 joerg *offset = first; 603 1.1 joerg *size = len; 604 1.1 joerg return (0); 605 1.1 joerg } 606 1.1 joerg 607 1.1 joerg 608 1.1 joerg /***************************************************************************** 609 1.1 joerg * Helper functions for authorization 610 1.1 joerg */ 611 1.1 joerg 612 1.1 joerg /* 613 1.1 joerg * Base64 encoding 614 1.1 joerg */ 615 1.1 joerg static char * 616 1.1 joerg http_base64(const char *src) 617 1.1 joerg { 618 1.1 joerg static const char base64[] = 619 1.1 joerg "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 620 1.1 joerg "abcdefghijklmnopqrstuvwxyz" 621 1.1 joerg "0123456789+/"; 622 1.1 joerg char *str, *dst; 623 1.1 joerg size_t l; 624 1.2 christos unsigned int t, r; 625 1.1 joerg 626 1.1 joerg l = strlen(src); 627 1.1 joerg if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL) 628 1.1 joerg return (NULL); 629 1.1 joerg dst = str; 630 1.1 joerg r = 0; 631 1.1 joerg 632 1.1 joerg while (l >= 3) { 633 1.1 joerg t = (src[0] << 16) | (src[1] << 8) | src[2]; 634 1.1 joerg dst[0] = base64[(t >> 18) & 0x3f]; 635 1.1 joerg dst[1] = base64[(t >> 12) & 0x3f]; 636 1.1 joerg dst[2] = base64[(t >> 6) & 0x3f]; 637 1.1 joerg dst[3] = base64[(t >> 0) & 0x3f]; 638 1.1 joerg src += 3; l -= 3; 639 1.1 joerg dst += 4; r += 4; 640 1.1 joerg } 641 1.1 joerg 642 1.1 joerg switch (l) { 643 1.1 joerg case 2: 644 1.1 joerg t = (src[0] << 16) | (src[1] << 8); 645 1.1 joerg dst[0] = base64[(t >> 18) & 0x3f]; 646 1.1 joerg dst[1] = base64[(t >> 12) & 0x3f]; 647 1.1 joerg dst[2] = base64[(t >> 6) & 0x3f]; 648 1.1 joerg dst[3] = '='; 649 1.1 joerg dst += 4; 650 1.1 joerg r += 4; 651 1.1 joerg break; 652 1.1 joerg case 1: 653 1.1 joerg t = src[0] << 16; 654 1.1 joerg dst[0] = base64[(t >> 18) & 0x3f]; 655 1.1 joerg dst[1] = base64[(t >> 12) & 0x3f]; 656 1.1 joerg dst[2] = dst[3] = '='; 657 1.1 joerg dst += 4; 658 1.1 joerg r += 4; 659 1.1 joerg break; 660 1.1 joerg case 0: 661 1.1 joerg break; 662 1.1 joerg } 663 1.1 joerg 664 1.1 joerg *dst = 0; 665 1.1 joerg return (str); 666 1.1 joerg } 667 1.1 joerg 668 1.1 joerg /* 669 1.1 joerg * Encode username and password 670 1.1 joerg */ 671 1.1 joerg static int 672 1.1 joerg http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd) 673 1.1 joerg { 674 1.1 joerg char *upw, *auth; 675 1.1 joerg int r; 676 1.1 joerg 677 1.1 joerg if (asprintf(&upw, "%s:%s", usr, pwd) == -1) 678 1.1 joerg return (-1); 679 1.1 joerg auth = http_base64(upw); 680 1.1 joerg free(upw); 681 1.1 joerg if (auth == NULL) 682 1.1 joerg return (-1); 683 1.2 christos r = http_cmd(conn, "%s: Basic %s\r\n", hdr, auth); 684 1.1 joerg free(auth); 685 1.1 joerg return (r); 686 1.1 joerg } 687 1.1 joerg 688 1.1 joerg /* 689 1.1 joerg * Send an authorization header 690 1.1 joerg */ 691 1.1 joerg static int 692 1.1 joerg http_authorize(conn_t *conn, const char *hdr, const char *p) 693 1.1 joerg { 694 1.1 joerg /* basic authorization */ 695 1.1 joerg if (strncasecmp(p, "basic:", 6) == 0) { 696 1.1 joerg char *user, *pwd, *str; 697 1.1 joerg int r; 698 1.1 joerg 699 1.1 joerg /* skip realm */ 700 1.1 joerg for (p += 6; *p && *p != ':'; ++p) 701 1.1 joerg /* nothing */ ; 702 1.1 joerg if (!*p || strchr(++p, ':') == NULL) 703 1.1 joerg return (-1); 704 1.1 joerg if ((str = strdup(p)) == NULL) 705 1.1 joerg return (-1); /* XXX */ 706 1.1 joerg user = str; 707 1.1 joerg pwd = strchr(str, ':'); 708 1.1 joerg *pwd++ = '\0'; 709 1.1 joerg r = http_basic_auth(conn, hdr, user, pwd); 710 1.1 joerg free(str); 711 1.1 joerg return (r); 712 1.1 joerg } 713 1.1 joerg return (-1); 714 1.1 joerg } 715 1.1 joerg 716 1.1 joerg 717 1.1 joerg /***************************************************************************** 718 1.1 joerg * Helper functions for connecting to a server or proxy 719 1.1 joerg */ 720 1.1 joerg 721 1.1 joerg /* 722 1.1 joerg * Connect to the correct HTTP server or proxy. 723 1.1 joerg */ 724 1.1 joerg static conn_t * 725 1.2 christos http_connect(struct url *URL, struct url *purl, const char *flags, int *cached) 726 1.1 joerg { 727 1.5 christos struct url *curl; 728 1.1 joerg conn_t *conn; 729 1.5 christos hdr_t h; 730 1.5 christos const char *p; 731 1.1 joerg int af, verbose; 732 1.5 christos #if defined(TCP_NOPUSH) && !defined(__APPLE__) 733 1.1 joerg int val; 734 1.1 joerg #endif 735 1.1 joerg 736 1.5 christos *cached = 0; 737 1.2 christos 738 1.1 joerg #ifdef INET6 739 1.1 joerg af = AF_UNSPEC; 740 1.1 joerg #else 741 1.1 joerg af = AF_INET; 742 1.1 joerg #endif 743 1.1 joerg 744 1.1 joerg verbose = CHECK_FLAG('v'); 745 1.1 joerg if (CHECK_FLAG('4')) 746 1.1 joerg af = AF_INET; 747 1.1 joerg #ifdef INET6 748 1.1 joerg else if (CHECK_FLAG('6')) 749 1.1 joerg af = AF_INET6; 750 1.1 joerg #endif 751 1.1 joerg 752 1.5 christos curl = (purl != NULL) ? purl : URL; 753 1.1 joerg if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { 754 1.1 joerg URL = purl; 755 1.1 joerg } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) { 756 1.1 joerg /* can't talk http to an ftp server */ 757 1.1 joerg /* XXX should set an error code */ 758 1.1 joerg return (NULL); 759 1.1 joerg } 760 1.1 joerg 761 1.5 christos if ((conn = fetch_cache_get(curl, af)) != NULL) { 762 1.2 christos *cached = 1; 763 1.2 christos return (conn); 764 1.2 christos } 765 1.2 christos 766 1.5 christos if ((conn = fetch_connect(curl, af, verbose)) == NULL) 767 1.1 joerg /* fetch_connect() has already set an error code */ 768 1.1 joerg return (NULL); 769 1.5 christos if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && purl) { 770 1.5 christos http_cmd(conn, "CONNECT %s:%d HTTP/1.1\r\n", 771 1.5 christos URL->host, URL->port); 772 1.5 christos http_cmd(conn, "Host: %s:%d\r\n", 773 1.5 christos URL->host, URL->port); 774 1.6 christos /* proxy authorization */ 775 1.6 christos if (*purl->user || *purl->pwd) 776 1.6 christos http_basic_auth(conn, "Proxy-Authorization", 777 1.6 christos purl->user, purl->pwd); 778 1.6 christos else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') 779 1.6 christos http_authorize(conn, "Proxy-Authorization", p); 780 1.5 christos http_cmd(conn, "\r\n"); 781 1.5 christos if (http_get_reply(conn) != HTTP_OK) { 782 1.5 christos http_seterr(conn->err); 783 1.5 christos goto ouch; 784 1.5 christos } 785 1.6 christos /* Read and discard the rest of the proxy response (if any) */ 786 1.5 christos do { 787 1.5 christos switch ((h = http_next_header(conn, &p))) { 788 1.5 christos case hdr_syserror: 789 1.5 christos fetch_syserr(); 790 1.5 christos goto ouch; 791 1.5 christos case hdr_error: 792 1.5 christos http_seterr(HTTP_PROTOCOL_ERROR); 793 1.5 christos goto ouch; 794 1.5 christos default: 795 1.5 christos /* ignore */ ; 796 1.5 christos } 797 1.6 christos } while (h > hdr_end); 798 1.5 christos } 799 1.1 joerg if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && 800 1.5 christos fetch_ssl(conn, URL, verbose) == -1) { 801 1.1 joerg /* grrr */ 802 1.1 joerg #ifdef EAUTH 803 1.1 joerg errno = EAUTH; 804 1.1 joerg #else 805 1.1 joerg errno = EPERM; 806 1.1 joerg #endif 807 1.5 christos goto ouch; 808 1.1 joerg } 809 1.1 joerg 810 1.5 christos #if defined(TCP_NOPUSH) && !defined(__APPLE__) 811 1.1 joerg val = 1; 812 1.1 joerg setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); 813 1.1 joerg #endif 814 1.1 joerg 815 1.1 joerg return (conn); 816 1.5 christos ouch: 817 1.5 christos fetch_close(conn); 818 1.5 christos return (NULL); 819 1.1 joerg } 820 1.1 joerg 821 1.1 joerg static struct url * 822 1.1 joerg http_get_proxy(struct url * url, const char *flags) 823 1.1 joerg { 824 1.1 joerg struct url *purl; 825 1.1 joerg char *p; 826 1.1 joerg 827 1.1 joerg if (flags != NULL && strchr(flags, 'd') != NULL) 828 1.1 joerg return (NULL); 829 1.1 joerg if (fetch_no_proxy_match(url->host)) 830 1.1 joerg return (NULL); 831 1.1 joerg if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 832 1.1 joerg *p && (purl = fetchParseURL(p))) { 833 1.1 joerg if (!*purl->scheme) 834 1.1 joerg strcpy(purl->scheme, SCHEME_HTTP); 835 1.1 joerg if (!purl->port) 836 1.1 joerg purl->port = fetch_default_proxy_port(purl->scheme); 837 1.1 joerg if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 838 1.1 joerg return (purl); 839 1.1 joerg fetchFreeURL(purl); 840 1.1 joerg } 841 1.1 joerg return (NULL); 842 1.1 joerg } 843 1.1 joerg 844 1.2 christos static void 845 1.2 christos set_if_modified_since(conn_t *conn, time_t last_modified) 846 1.2 christos { 847 1.2 christos static const char weekdays[] = "SunMonTueWedThuFriSat"; 848 1.2 christos static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 849 1.2 christos struct tm tm; 850 1.2 christos char buf[80]; 851 1.2 christos gmtime_r(&last_modified, &tm); 852 1.5 christos snprintf(buf, sizeof(buf), "%.3s, %02d %.3s %4ld %02d:%02d:%02d GMT", 853 1.2 christos weekdays + tm.tm_wday * 3, tm.tm_mday, months + tm.tm_mon * 3, 854 1.5 christos (long)tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); 855 1.2 christos http_cmd(conn, "If-Modified-Since: %s\r\n", buf); 856 1.2 christos } 857 1.2 christos 858 1.2 christos 859 1.1 joerg /***************************************************************************** 860 1.1 joerg * Core 861 1.1 joerg */ 862 1.1 joerg 863 1.1 joerg /* 864 1.1 joerg * Send a request and process the reply 865 1.1 joerg * 866 1.1 joerg * XXX This function is way too long, the do..while loop should be split 867 1.1 joerg * XXX off into a separate function. 868 1.1 joerg */ 869 1.1 joerg fetchIO * 870 1.1 joerg http_request(struct url *URL, const char *op, struct url_stat *us, 871 1.1 joerg struct url *purl, const char *flags) 872 1.1 joerg { 873 1.1 joerg conn_t *conn; 874 1.1 joerg struct url *url, *new; 875 1.2 christos int chunked, direct, if_modified_since, need_auth, noredirect; 876 1.2 christos int keep_alive, verbose, cached; 877 1.1 joerg int e, i, n, val; 878 1.1 joerg off_t offset, clength, length, size; 879 1.1 joerg time_t mtime; 880 1.1 joerg const char *p; 881 1.1 joerg fetchIO *f; 882 1.1 joerg hdr_t h; 883 1.1 joerg char hbuf[URL_HOSTLEN + 7], *host; 884 1.1 joerg 885 1.1 joerg direct = CHECK_FLAG('d'); 886 1.1 joerg noredirect = CHECK_FLAG('A'); 887 1.1 joerg verbose = CHECK_FLAG('v'); 888 1.2 christos if_modified_since = CHECK_FLAG('i'); 889 1.2 christos keep_alive = 0; 890 1.1 joerg 891 1.1 joerg if (direct && purl) { 892 1.1 joerg fetchFreeURL(purl); 893 1.1 joerg purl = NULL; 894 1.1 joerg } 895 1.1 joerg 896 1.1 joerg /* try the provided URL first */ 897 1.1 joerg url = URL; 898 1.1 joerg 899 1.1 joerg /* if the A flag is set, we only get one try */ 900 1.1 joerg n = noredirect ? 1 : MAX_REDIRECT; 901 1.1 joerg i = 0; 902 1.1 joerg 903 1.1 joerg e = HTTP_PROTOCOL_ERROR; 904 1.1 joerg need_auth = 0; 905 1.1 joerg do { 906 1.1 joerg new = NULL; 907 1.1 joerg chunked = 0; 908 1.1 joerg offset = 0; 909 1.1 joerg clength = -1; 910 1.1 joerg length = -1; 911 1.1 joerg size = -1; 912 1.1 joerg mtime = 0; 913 1.1 joerg 914 1.1 joerg /* check port */ 915 1.1 joerg if (!url->port) 916 1.1 joerg url->port = fetch_default_port(url->scheme); 917 1.1 joerg 918 1.1 joerg /* were we redirected to an FTP URL? */ 919 1.1 joerg if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) { 920 1.1 joerg if (strcmp(op, "GET") == 0) 921 1.1 joerg return (ftp_request(url, "RETR", NULL, us, purl, flags)); 922 1.1 joerg else if (strcmp(op, "HEAD") == 0) 923 1.1 joerg return (ftp_request(url, "STAT", NULL, us, purl, flags)); 924 1.1 joerg } 925 1.1 joerg 926 1.1 joerg /* connect to server or proxy */ 927 1.2 christos if ((conn = http_connect(url, purl, flags, &cached)) == NULL) 928 1.1 joerg goto ouch; 929 1.1 joerg 930 1.1 joerg host = url->host; 931 1.1 joerg #ifdef INET6 932 1.1 joerg if (strchr(url->host, ':')) { 933 1.1 joerg snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); 934 1.1 joerg host = hbuf; 935 1.1 joerg } 936 1.1 joerg #endif 937 1.1 joerg if (url->port != fetch_default_port(url->scheme)) { 938 1.1 joerg if (host != hbuf) { 939 1.1 joerg strcpy(hbuf, host); 940 1.1 joerg host = hbuf; 941 1.1 joerg } 942 1.1 joerg snprintf(hbuf + strlen(hbuf), 943 1.1 joerg sizeof(hbuf) - strlen(hbuf), ":%d", url->port); 944 1.1 joerg } 945 1.1 joerg 946 1.1 joerg /* send request */ 947 1.1 joerg if (verbose) 948 1.1 joerg fetch_info("requesting %s://%s%s", 949 1.1 joerg url->scheme, host, url->doc); 950 1.5 christos if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { 951 1.2 christos http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n", 952 1.1 joerg op, url->scheme, host, url->doc); 953 1.1 joerg } else { 954 1.2 christos http_cmd(conn, "%s %s HTTP/1.1\r\n", 955 1.1 joerg op, url->doc); 956 1.1 joerg } 957 1.1 joerg 958 1.2 christos if (if_modified_since && url->last_modified > 0) 959 1.2 christos set_if_modified_since(conn, url->last_modified); 960 1.2 christos 961 1.1 joerg /* virtual host */ 962 1.2 christos http_cmd(conn, "Host: %s\r\n", host); 963 1.1 joerg 964 1.1 joerg /* proxy authorization */ 965 1.1 joerg if (purl) { 966 1.1 joerg if (*purl->user || *purl->pwd) 967 1.1 joerg http_basic_auth(conn, "Proxy-Authorization", 968 1.1 joerg purl->user, purl->pwd); 969 1.1 joerg else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') 970 1.1 joerg http_authorize(conn, "Proxy-Authorization", p); 971 1.1 joerg } 972 1.1 joerg 973 1.1 joerg /* server authorization */ 974 1.1 joerg if (need_auth || *url->user || *url->pwd) { 975 1.1 joerg if (*url->user || *url->pwd) 976 1.1 joerg http_basic_auth(conn, "Authorization", url->user, url->pwd); 977 1.1 joerg else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') 978 1.1 joerg http_authorize(conn, "Authorization", p); 979 1.1 joerg else if (fetchAuthMethod && fetchAuthMethod(url) == 0) { 980 1.1 joerg http_basic_auth(conn, "Authorization", url->user, url->pwd); 981 1.1 joerg } else { 982 1.1 joerg http_seterr(HTTP_NEED_AUTH); 983 1.1 joerg goto ouch; 984 1.1 joerg } 985 1.1 joerg } 986 1.1 joerg 987 1.1 joerg /* other headers */ 988 1.1 joerg if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') { 989 1.1 joerg if (strcasecmp(p, "auto") == 0) 990 1.2 christos http_cmd(conn, "Referer: %s://%s%s\r\n", 991 1.1 joerg url->scheme, host, url->doc); 992 1.1 joerg else 993 1.2 christos http_cmd(conn, "Referer: %s\r\n", p); 994 1.1 joerg } 995 1.1 joerg if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') 996 1.2 christos http_cmd(conn, "User-Agent: %s\r\n", p); 997 1.1 joerg else 998 1.2 christos http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER); 999 1.1 joerg if (url->offset > 0) 1000 1.2 christos http_cmd(conn, "Range: bytes=%lld-\r\n", (long long)url->offset); 1001 1.2 christos http_cmd(conn, "\r\n"); 1002 1.1 joerg 1003 1.1 joerg /* 1004 1.1 joerg * Force the queued request to be dispatched. Normally, one 1005 1.1 joerg * would do this with shutdown(2) but squid proxies can be 1006 1.1 joerg * configured to disallow such half-closed connections. To 1007 1.1 joerg * be compatible with such configurations, fiddle with socket 1008 1.1 joerg * options to force the pending data to be written. 1009 1.1 joerg */ 1010 1.5 christos #if defined(TCP_NOPUSH) && !defined(__APPLE__) 1011 1.1 joerg val = 0; 1012 1.1 joerg setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, 1013 1.1 joerg sizeof(val)); 1014 1.1 joerg #endif 1015 1.1 joerg val = 1; 1016 1.1 joerg setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, 1017 1.2 christos (socklen_t)sizeof(val)); 1018 1.1 joerg 1019 1.1 joerg /* get reply */ 1020 1.1 joerg switch (http_get_reply(conn)) { 1021 1.1 joerg case HTTP_OK: 1022 1.1 joerg case HTTP_PARTIAL: 1023 1.2 christos case HTTP_NOT_MODIFIED: 1024 1.1 joerg /* fine */ 1025 1.1 joerg break; 1026 1.1 joerg case HTTP_MOVED_PERM: 1027 1.1 joerg case HTTP_MOVED_TEMP: 1028 1.1 joerg case HTTP_SEE_OTHER: 1029 1.1 joerg /* 1030 1.1 joerg * Not so fine, but we still have to read the 1031 1.1 joerg * headers to get the new location. 1032 1.1 joerg */ 1033 1.1 joerg break; 1034 1.1 joerg case HTTP_NEED_AUTH: 1035 1.1 joerg if (need_auth) { 1036 1.1 joerg /* 1037 1.1 joerg * We already sent out authorization code, 1038 1.1 joerg * so there's nothing more we can do. 1039 1.1 joerg */ 1040 1.1 joerg http_seterr(conn->err); 1041 1.1 joerg goto ouch; 1042 1.1 joerg } 1043 1.1 joerg /* try again, but send the password this time */ 1044 1.1 joerg if (verbose) 1045 1.1 joerg fetch_info("server requires authorization"); 1046 1.1 joerg break; 1047 1.1 joerg case HTTP_NEED_PROXY_AUTH: 1048 1.1 joerg /* 1049 1.1 joerg * If we're talking to a proxy, we already sent 1050 1.1 joerg * our proxy authorization code, so there's 1051 1.1 joerg * nothing more we can do. 1052 1.1 joerg */ 1053 1.1 joerg http_seterr(conn->err); 1054 1.1 joerg goto ouch; 1055 1.1 joerg case HTTP_BAD_RANGE: 1056 1.1 joerg /* 1057 1.1 joerg * This can happen if we ask for 0 bytes because 1058 1.1 joerg * we already have the whole file. Consider this 1059 1.1 joerg * a success for now, and check sizes later. 1060 1.1 joerg */ 1061 1.1 joerg break; 1062 1.1 joerg case HTTP_PROTOCOL_ERROR: 1063 1.1 joerg /* fall through */ 1064 1.1 joerg case -1: 1065 1.2 christos --i; 1066 1.2 christos if (cached) 1067 1.2 christos continue; 1068 1.1 joerg fetch_syserr(); 1069 1.1 joerg goto ouch; 1070 1.1 joerg default: 1071 1.1 joerg http_seterr(conn->err); 1072 1.1 joerg if (!verbose) 1073 1.1 joerg goto ouch; 1074 1.1 joerg /* fall through so we can get the full error message */ 1075 1.1 joerg } 1076 1.1 joerg 1077 1.1 joerg /* get headers */ 1078 1.1 joerg do { 1079 1.1 joerg switch ((h = http_next_header(conn, &p))) { 1080 1.1 joerg case hdr_syserror: 1081 1.1 joerg fetch_syserr(); 1082 1.1 joerg goto ouch; 1083 1.1 joerg case hdr_error: 1084 1.1 joerg http_seterr(HTTP_PROTOCOL_ERROR); 1085 1.1 joerg goto ouch; 1086 1.2 christos case hdr_connection: 1087 1.2 christos /* XXX too weak? */ 1088 1.2 christos keep_alive = (strcasecmp(p, "keep-alive") == 0); 1089 1.2 christos break; 1090 1.1 joerg case hdr_content_length: 1091 1.1 joerg http_parse_length(p, &clength); 1092 1.1 joerg break; 1093 1.1 joerg case hdr_content_range: 1094 1.1 joerg http_parse_range(p, &offset, &length, &size); 1095 1.1 joerg break; 1096 1.1 joerg case hdr_last_modified: 1097 1.1 joerg http_parse_mtime(p, &mtime); 1098 1.1 joerg break; 1099 1.1 joerg case hdr_location: 1100 1.1 joerg if (!HTTP_REDIRECT(conn->err)) 1101 1.1 joerg break; 1102 1.1 joerg if (new) 1103 1.1 joerg free(new); 1104 1.1 joerg if (verbose) 1105 1.1 joerg fetch_info("%d redirect to %s", conn->err, p); 1106 1.1 joerg if (*p == '/') 1107 1.1 joerg /* absolute path */ 1108 1.1 joerg new = fetchMakeURL(url->scheme, url->host, url->port, p, 1109 1.1 joerg url->user, url->pwd); 1110 1.1 joerg else 1111 1.1 joerg new = fetchParseURL(p); 1112 1.1 joerg if (new == NULL) { 1113 1.1 joerg /* XXX should set an error code */ 1114 1.1 joerg goto ouch; 1115 1.1 joerg } 1116 1.1 joerg if (!*new->user && !*new->pwd) { 1117 1.1 joerg strcpy(new->user, url->user); 1118 1.1 joerg strcpy(new->pwd, url->pwd); 1119 1.1 joerg } 1120 1.1 joerg new->offset = url->offset; 1121 1.1 joerg new->length = url->length; 1122 1.1 joerg break; 1123 1.1 joerg case hdr_transfer_encoding: 1124 1.1 joerg /* XXX weak test*/ 1125 1.1 joerg chunked = (strcasecmp(p, "chunked") == 0); 1126 1.1 joerg break; 1127 1.1 joerg case hdr_www_authenticate: 1128 1.1 joerg if (conn->err != HTTP_NEED_AUTH) 1129 1.1 joerg break; 1130 1.1 joerg /* if we were smarter, we'd check the method and realm */ 1131 1.1 joerg break; 1132 1.1 joerg case hdr_end: 1133 1.1 joerg /* fall through */ 1134 1.1 joerg case hdr_unknown: 1135 1.1 joerg /* ignore */ 1136 1.1 joerg break; 1137 1.1 joerg } 1138 1.1 joerg } while (h > hdr_end); 1139 1.1 joerg 1140 1.1 joerg /* we need to provide authentication */ 1141 1.1 joerg if (conn->err == HTTP_NEED_AUTH) { 1142 1.1 joerg e = conn->err; 1143 1.1 joerg need_auth = 1; 1144 1.1 joerg fetch_close(conn); 1145 1.1 joerg conn = NULL; 1146 1.1 joerg continue; 1147 1.1 joerg } 1148 1.1 joerg 1149 1.1 joerg /* requested range not satisfiable */ 1150 1.1 joerg if (conn->err == HTTP_BAD_RANGE) { 1151 1.1 joerg if (url->offset == size && url->length == 0) { 1152 1.1 joerg /* asked for 0 bytes; fake it */ 1153 1.1 joerg offset = url->offset; 1154 1.1 joerg conn->err = HTTP_OK; 1155 1.1 joerg break; 1156 1.1 joerg } else { 1157 1.1 joerg http_seterr(conn->err); 1158 1.1 joerg goto ouch; 1159 1.1 joerg } 1160 1.1 joerg } 1161 1.1 joerg 1162 1.1 joerg /* we have a hit or an error */ 1163 1.2 christos if (conn->err == HTTP_OK || 1164 1.2 christos conn->err == HTTP_PARTIAL || 1165 1.2 christos conn->err == HTTP_NOT_MODIFIED || 1166 1.2 christos HTTP_ERROR(conn->err)) 1167 1.1 joerg break; 1168 1.1 joerg 1169 1.1 joerg /* all other cases: we got a redirect */ 1170 1.1 joerg e = conn->err; 1171 1.1 joerg need_auth = 0; 1172 1.1 joerg fetch_close(conn); 1173 1.1 joerg conn = NULL; 1174 1.1 joerg if (!new) 1175 1.1 joerg break; 1176 1.1 joerg if (url != URL) 1177 1.1 joerg fetchFreeURL(url); 1178 1.1 joerg url = new; 1179 1.1 joerg } while (++i < n); 1180 1.1 joerg 1181 1.1 joerg /* we failed, or ran out of retries */ 1182 1.1 joerg if (conn == NULL) { 1183 1.1 joerg http_seterr(e); 1184 1.1 joerg goto ouch; 1185 1.1 joerg } 1186 1.1 joerg 1187 1.1 joerg /* check for inconsistencies */ 1188 1.1 joerg if (clength != -1 && length != -1 && clength != length) { 1189 1.1 joerg http_seterr(HTTP_PROTOCOL_ERROR); 1190 1.1 joerg goto ouch; 1191 1.1 joerg } 1192 1.1 joerg if (clength == -1) 1193 1.1 joerg clength = length; 1194 1.1 joerg if (clength != -1) 1195 1.1 joerg length = offset + clength; 1196 1.1 joerg if (length != -1 && size != -1 && length != size) { 1197 1.1 joerg http_seterr(HTTP_PROTOCOL_ERROR); 1198 1.1 joerg goto ouch; 1199 1.1 joerg } 1200 1.1 joerg if (size == -1) 1201 1.1 joerg size = length; 1202 1.1 joerg 1203 1.1 joerg /* fill in stats */ 1204 1.1 joerg if (us) { 1205 1.1 joerg us->size = size; 1206 1.1 joerg us->atime = us->mtime = mtime; 1207 1.1 joerg } 1208 1.1 joerg 1209 1.1 joerg /* too far? */ 1210 1.1 joerg if (URL->offset > 0 && offset > URL->offset) { 1211 1.1 joerg http_seterr(HTTP_PROTOCOL_ERROR); 1212 1.1 joerg goto ouch; 1213 1.1 joerg } 1214 1.1 joerg 1215 1.1 joerg /* report back real offset and size */ 1216 1.1 joerg URL->offset = offset; 1217 1.1 joerg URL->length = clength; 1218 1.1 joerg 1219 1.2 christos if (clength == -1 && !chunked) 1220 1.2 christos keep_alive = 0; 1221 1.2 christos 1222 1.2 christos if (conn->err == HTTP_NOT_MODIFIED) { 1223 1.2 christos http_seterr(HTTP_NOT_MODIFIED); 1224 1.2 christos if (keep_alive) { 1225 1.2 christos fetch_cache_put(conn, fetch_close); 1226 1.2 christos conn = NULL; 1227 1.2 christos } 1228 1.2 christos goto ouch; 1229 1.2 christos } 1230 1.2 christos 1231 1.1 joerg /* wrap it up in a fetchIO */ 1232 1.2 christos if ((f = http_funopen(conn, chunked, keep_alive, clength)) == NULL) { 1233 1.1 joerg fetch_syserr(); 1234 1.1 joerg goto ouch; 1235 1.1 joerg } 1236 1.1 joerg 1237 1.1 joerg if (url != URL) 1238 1.1 joerg fetchFreeURL(url); 1239 1.1 joerg if (purl) 1240 1.1 joerg fetchFreeURL(purl); 1241 1.1 joerg 1242 1.1 joerg if (HTTP_ERROR(conn->err)) { 1243 1.2 christos 1244 1.2 christos if (keep_alive) { 1245 1.2 christos char buf[512]; 1246 1.2 christos do { 1247 1.2 christos } while (fetchIO_read(f, buf, sizeof(buf)) > 0); 1248 1.2 christos } 1249 1.2 christos 1250 1.1 joerg fetchIO_close(f); 1251 1.1 joerg f = NULL; 1252 1.1 joerg } 1253 1.1 joerg 1254 1.1 joerg return (f); 1255 1.1 joerg 1256 1.1 joerg ouch: 1257 1.1 joerg if (url != URL) 1258 1.1 joerg fetchFreeURL(url); 1259 1.1 joerg if (purl) 1260 1.1 joerg fetchFreeURL(purl); 1261 1.1 joerg if (conn != NULL) 1262 1.1 joerg fetch_close(conn); 1263 1.1 joerg return (NULL); 1264 1.1 joerg } 1265 1.1 joerg 1266 1.1 joerg 1267 1.1 joerg /***************************************************************************** 1268 1.1 joerg * Entry points 1269 1.1 joerg */ 1270 1.1 joerg 1271 1.1 joerg /* 1272 1.1 joerg * Retrieve and stat a file by HTTP 1273 1.1 joerg */ 1274 1.1 joerg fetchIO * 1275 1.1 joerg fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags) 1276 1.1 joerg { 1277 1.1 joerg return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags)); 1278 1.1 joerg } 1279 1.1 joerg 1280 1.1 joerg /* 1281 1.1 joerg * Retrieve a file by HTTP 1282 1.1 joerg */ 1283 1.1 joerg fetchIO * 1284 1.1 joerg fetchGetHTTP(struct url *URL, const char *flags) 1285 1.1 joerg { 1286 1.1 joerg return (fetchXGetHTTP(URL, NULL, flags)); 1287 1.1 joerg } 1288 1.1 joerg 1289 1.1 joerg /* 1290 1.1 joerg * Store a file by HTTP 1291 1.1 joerg */ 1292 1.1 joerg fetchIO * 1293 1.2 christos /*ARGSUSED*/ 1294 1.2 christos fetchPutHTTP(struct url *URL __unused, const char *flags __unused) 1295 1.1 joerg { 1296 1.1 joerg fprintf(stderr, "fetchPutHTTP(): not implemented\n"); 1297 1.1 joerg return (NULL); 1298 1.1 joerg } 1299 1.1 joerg 1300 1.1 joerg /* 1301 1.1 joerg * Get an HTTP document's metadata 1302 1.1 joerg */ 1303 1.1 joerg int 1304 1.1 joerg fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags) 1305 1.1 joerg { 1306 1.1 joerg fetchIO *f; 1307 1.1 joerg 1308 1.1 joerg f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags); 1309 1.1 joerg if (f == NULL) 1310 1.1 joerg return (-1); 1311 1.1 joerg fetchIO_close(f); 1312 1.1 joerg return (0); 1313 1.1 joerg } 1314 1.1 joerg 1315 1.1 joerg enum http_states { 1316 1.1 joerg ST_NONE, 1317 1.1 joerg ST_LT, 1318 1.1 joerg ST_LTA, 1319 1.1 joerg ST_TAGA, 1320 1.1 joerg ST_H, 1321 1.1 joerg ST_R, 1322 1.1 joerg ST_E, 1323 1.1 joerg ST_F, 1324 1.1 joerg ST_HREF, 1325 1.1 joerg ST_HREFQ, 1326 1.1 joerg ST_TAG, 1327 1.1 joerg ST_TAGAX, 1328 1.1 joerg ST_TAGAQ 1329 1.1 joerg }; 1330 1.1 joerg 1331 1.1 joerg struct index_parser { 1332 1.1 joerg struct url_list *ue; 1333 1.1 joerg struct url *url; 1334 1.1 joerg enum http_states state; 1335 1.1 joerg }; 1336 1.1 joerg 1337 1.2 christos static ssize_t 1338 1.1 joerg parse_index(struct index_parser *parser, const char *buf, size_t len) 1339 1.1 joerg { 1340 1.1 joerg char *end_attr, p = *buf; 1341 1.1 joerg 1342 1.1 joerg switch (parser->state) { 1343 1.1 joerg case ST_NONE: 1344 1.1 joerg /* Plain text, not in markup */ 1345 1.1 joerg if (p == '<') 1346 1.1 joerg parser->state = ST_LT; 1347 1.1 joerg return 1; 1348 1.1 joerg case ST_LT: 1349 1.1 joerg /* In tag -- "<" already found */ 1350 1.1 joerg if (p == '>') 1351 1.1 joerg parser->state = ST_NONE; 1352 1.1 joerg else if (p == 'a' || p == 'A') 1353 1.1 joerg parser->state = ST_LTA; 1354 1.1 joerg else if (!isspace((unsigned char)p)) 1355 1.1 joerg parser->state = ST_TAG; 1356 1.1 joerg return 1; 1357 1.1 joerg case ST_LTA: 1358 1.1 joerg /* In tag -- "<a" already found */ 1359 1.1 joerg if (p == '>') 1360 1.1 joerg parser->state = ST_NONE; 1361 1.1 joerg else if (p == '"') 1362 1.1 joerg parser->state = ST_TAGAQ; 1363 1.1 joerg else if (isspace((unsigned char)p)) 1364 1.1 joerg parser->state = ST_TAGA; 1365 1.1 joerg else 1366 1.1 joerg parser->state = ST_TAG; 1367 1.1 joerg return 1; 1368 1.1 joerg case ST_TAG: 1369 1.1 joerg /* In tag, but not "<a" -- disregard */ 1370 1.1 joerg if (p == '>') 1371 1.1 joerg parser->state = ST_NONE; 1372 1.1 joerg return 1; 1373 1.1 joerg case ST_TAGA: 1374 1.1 joerg /* In a-tag -- "<a " already found */ 1375 1.1 joerg if (p == '>') 1376 1.1 joerg parser->state = ST_NONE; 1377 1.1 joerg else if (p == '"') 1378 1.1 joerg parser->state = ST_TAGAQ; 1379 1.1 joerg else if (p == 'h' || p == 'H') 1380 1.1 joerg parser->state = ST_H; 1381 1.1 joerg else if (!isspace((unsigned char)p)) 1382 1.1 joerg parser->state = ST_TAGAX; 1383 1.1 joerg return 1; 1384 1.1 joerg case ST_TAGAX: 1385 1.1 joerg /* In unknown keyword in a-tag */ 1386 1.1 joerg if (p == '>') 1387 1.1 joerg parser->state = ST_NONE; 1388 1.1 joerg else if (p == '"') 1389 1.1 joerg parser->state = ST_TAGAQ; 1390 1.1 joerg else if (isspace((unsigned char)p)) 1391 1.1 joerg parser->state = ST_TAGA; 1392 1.1 joerg return 1; 1393 1.1 joerg case ST_TAGAQ: 1394 1.1 joerg /* In a-tag, unknown argument for keys. */ 1395 1.1 joerg if (p == '>') 1396 1.1 joerg parser->state = ST_NONE; 1397 1.1 joerg else if (p == '"') 1398 1.1 joerg parser->state = ST_TAGA; 1399 1.1 joerg return 1; 1400 1.1 joerg case ST_H: 1401 1.1 joerg /* In a-tag -- "<a h" already found */ 1402 1.1 joerg if (p == '>') 1403 1.1 joerg parser->state = ST_NONE; 1404 1.1 joerg else if (p == '"') 1405 1.1 joerg parser->state = ST_TAGAQ; 1406 1.1 joerg else if (p == 'r' || p == 'R') 1407 1.1 joerg parser->state = ST_R; 1408 1.1 joerg else if (isspace((unsigned char)p)) 1409 1.1 joerg parser->state = ST_TAGA; 1410 1.1 joerg else 1411 1.1 joerg parser->state = ST_TAGAX; 1412 1.1 joerg return 1; 1413 1.1 joerg case ST_R: 1414 1.1 joerg /* In a-tag -- "<a hr" already found */ 1415 1.1 joerg if (p == '>') 1416 1.1 joerg parser->state = ST_NONE; 1417 1.1 joerg else if (p == '"') 1418 1.1 joerg parser->state = ST_TAGAQ; 1419 1.1 joerg else if (p == 'e' || p == 'E') 1420 1.1 joerg parser->state = ST_E; 1421 1.1 joerg else if (isspace((unsigned char)p)) 1422 1.1 joerg parser->state = ST_TAGA; 1423 1.1 joerg else 1424 1.1 joerg parser->state = ST_TAGAX; 1425 1.1 joerg return 1; 1426 1.1 joerg case ST_E: 1427 1.1 joerg /* In a-tag -- "<a hre" already found */ 1428 1.1 joerg if (p == '>') 1429 1.1 joerg parser->state = ST_NONE; 1430 1.1 joerg else if (p == '"') 1431 1.1 joerg parser->state = ST_TAGAQ; 1432 1.1 joerg else if (p == 'f' || p == 'F') 1433 1.1 joerg parser->state = ST_F; 1434 1.1 joerg else if (isspace((unsigned char)p)) 1435 1.1 joerg parser->state = ST_TAGA; 1436 1.1 joerg else 1437 1.1 joerg parser->state = ST_TAGAX; 1438 1.1 joerg return 1; 1439 1.1 joerg case ST_F: 1440 1.1 joerg /* In a-tag -- "<a href" already found */ 1441 1.1 joerg if (p == '>') 1442 1.1 joerg parser->state = ST_NONE; 1443 1.1 joerg else if (p == '"') 1444 1.1 joerg parser->state = ST_TAGAQ; 1445 1.1 joerg else if (p == '=') 1446 1.1 joerg parser->state = ST_HREF; 1447 1.1 joerg else if (!isspace((unsigned char)p)) 1448 1.1 joerg parser->state = ST_TAGAX; 1449 1.1 joerg return 1; 1450 1.1 joerg case ST_HREF: 1451 1.1 joerg /* In a-tag -- "<a href=" already found */ 1452 1.1 joerg if (p == '>') 1453 1.1 joerg parser->state = ST_NONE; 1454 1.1 joerg else if (p == '"') 1455 1.1 joerg parser->state = ST_HREFQ; 1456 1.1 joerg else if (!isspace((unsigned char)p)) 1457 1.1 joerg parser->state = ST_TAGA; 1458 1.1 joerg return 1; 1459 1.1 joerg case ST_HREFQ: 1460 1.1 joerg /* In href of the a-tag */ 1461 1.1 joerg end_attr = memchr(buf, '"', len); 1462 1.1 joerg if (end_attr == NULL) 1463 1.1 joerg return 0; 1464 1.1 joerg *end_attr = '\0'; 1465 1.1 joerg parser->state = ST_TAGA; 1466 1.2 christos if (fetch_add_entry(parser->ue, parser->url, buf, 1)) 1467 1.2 christos return -1; 1468 1.1 joerg return end_attr + 1 - buf; 1469 1.1 joerg } 1470 1.2 christos /* NOTREACHED */ 1471 1.1 joerg abort(); 1472 1.1 joerg } 1473 1.1 joerg 1474 1.2 christos struct http_index_cache { 1475 1.2 christos struct http_index_cache *next; 1476 1.2 christos struct url *location; 1477 1.2 christos struct url_list ue; 1478 1.2 christos }; 1479 1.2 christos 1480 1.2 christos static struct http_index_cache *index_cache; 1481 1.2 christos 1482 1.1 joerg /* 1483 1.1 joerg * List a directory 1484 1.1 joerg */ 1485 1.1 joerg int 1486 1.2 christos /*ARGSUSED*/ 1487 1.2 christos fetchListHTTP(struct url_list *ue, struct url *url, const char *pattern __unused, const char *flags) 1488 1.1 joerg { 1489 1.1 joerg fetchIO *f; 1490 1.1 joerg char buf[2 * PATH_MAX]; 1491 1.2 christos size_t buf_len, sum_processed; 1492 1.2 christos ssize_t read_len, processed; 1493 1.1 joerg struct index_parser state; 1494 1.2 christos struct http_index_cache *cache = NULL; 1495 1.2 christos int do_cache, ret; 1496 1.2 christos 1497 1.2 christos do_cache = CHECK_FLAG('c'); 1498 1.1 joerg 1499 1.2 christos if (do_cache) { 1500 1.2 christos for (cache = index_cache; cache != NULL; cache = cache->next) { 1501 1.2 christos if (strcmp(cache->location->scheme, url->scheme)) 1502 1.2 christos continue; 1503 1.2 christos if (strcmp(cache->location->user, url->user)) 1504 1.2 christos continue; 1505 1.2 christos if (strcmp(cache->location->pwd, url->pwd)) 1506 1.2 christos continue; 1507 1.2 christos if (strcmp(cache->location->host, url->host)) 1508 1.2 christos continue; 1509 1.2 christos if (cache->location->port != url->port) 1510 1.2 christos continue; 1511 1.2 christos if (strcmp(cache->location->doc, url->doc)) 1512 1.2 christos continue; 1513 1.2 christos return fetchAppendURLList(ue, &cache->ue); 1514 1.2 christos } 1515 1.2 christos 1516 1.2 christos cache = malloc(sizeof(*cache)); 1517 1.2 christos fetchInitURLList(&cache->ue); 1518 1.2 christos cache->location = fetchCopyURL(url); 1519 1.2 christos } 1520 1.1 joerg 1521 1.1 joerg f = fetchGetHTTP(url, flags); 1522 1.2 christos if (f == NULL) { 1523 1.2 christos if (do_cache) { 1524 1.2 christos fetchFreeURLList(&cache->ue); 1525 1.2 christos fetchFreeURL(cache->location); 1526 1.2 christos free(cache); 1527 1.2 christos } 1528 1.1 joerg return -1; 1529 1.2 christos } 1530 1.2 christos 1531 1.2 christos state.url = url; 1532 1.2 christos state.state = ST_NONE; 1533 1.2 christos if (do_cache) { 1534 1.2 christos state.ue = &cache->ue; 1535 1.2 christos } else { 1536 1.2 christos state.ue = ue; 1537 1.2 christos } 1538 1.1 joerg 1539 1.1 joerg buf_len = 0; 1540 1.1 joerg 1541 1.1 joerg while ((read_len = fetchIO_read(f, buf + buf_len, sizeof(buf) - buf_len)) > 0) { 1542 1.1 joerg buf_len += read_len; 1543 1.1 joerg sum_processed = 0; 1544 1.1 joerg do { 1545 1.1 joerg processed = parse_index(&state, buf + sum_processed, buf_len); 1546 1.2 christos if (processed == -1) 1547 1.2 christos break; 1548 1.1 joerg buf_len -= processed; 1549 1.1 joerg sum_processed += processed; 1550 1.1 joerg } while (processed != 0 && buf_len > 0); 1551 1.2 christos if (processed == -1) { 1552 1.2 christos read_len = -1; 1553 1.2 christos break; 1554 1.2 christos } 1555 1.1 joerg memmove(buf, buf + sum_processed, buf_len); 1556 1.1 joerg } 1557 1.1 joerg 1558 1.1 joerg fetchIO_close(f); 1559 1.2 christos 1560 1.2 christos ret = read_len < 0 ? -1 : 0; 1561 1.2 christos 1562 1.2 christos if (do_cache) { 1563 1.2 christos if (ret == 0) { 1564 1.2 christos cache->next = index_cache; 1565 1.2 christos index_cache = cache; 1566 1.2 christos } 1567 1.2 christos 1568 1.2 christos if (fetchAppendURLList(ue, &cache->ue)) 1569 1.2 christos ret = -1; 1570 1.2 christos } 1571 1.2 christos 1572 1.2 christos return ret; 1573 1.1 joerg } 1574