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