xref: /minix3/external/bsd/fetch/dist/libfetch/http.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: http.c,v 1.3 2014/01/07 02:13:00 joerg Exp $	*/
2040ec644SDavid van Moolenbroek /*-
3040ec644SDavid van Moolenbroek  * Copyright (c) 2000-2004 Dag-Erling Co�dan Sm�rgrav
4040ec644SDavid van Moolenbroek  * Copyright (c) 2003 Thomas Klausner <wiz@NetBSD.org>
5040ec644SDavid van Moolenbroek  * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>
6040ec644SDavid van Moolenbroek  * All rights reserved.
7040ec644SDavid van Moolenbroek  *
8040ec644SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
9040ec644SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
10040ec644SDavid van Moolenbroek  * are met:
11040ec644SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
12040ec644SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer
13040ec644SDavid van Moolenbroek  *    in this position and unchanged.
14040ec644SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
15040ec644SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
16040ec644SDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
17040ec644SDavid van Moolenbroek  * 3. The name of the author may not be used to endorse or promote products
18040ec644SDavid van Moolenbroek  *    derived from this software without specific prior written permission.
19040ec644SDavid van Moolenbroek  *
20040ec644SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21040ec644SDavid van Moolenbroek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22040ec644SDavid van Moolenbroek  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23040ec644SDavid van Moolenbroek  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24040ec644SDavid van Moolenbroek  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25040ec644SDavid van Moolenbroek  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26040ec644SDavid van Moolenbroek  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27040ec644SDavid van Moolenbroek  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28040ec644SDavid van Moolenbroek  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29040ec644SDavid van Moolenbroek  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30040ec644SDavid van Moolenbroek  *
31040ec644SDavid van Moolenbroek  * $FreeBSD: http.c,v 1.83 2008/02/06 11:39:55 des Exp $
32040ec644SDavid van Moolenbroek  */
33040ec644SDavid van Moolenbroek 
34040ec644SDavid van Moolenbroek /*
35040ec644SDavid van Moolenbroek  * The following copyright applies to the base64 code:
36040ec644SDavid van Moolenbroek  *
37040ec644SDavid van Moolenbroek  *-
38040ec644SDavid van Moolenbroek  * Copyright 1997 Massachusetts Institute of Technology
39040ec644SDavid van Moolenbroek  *
40040ec644SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software and
41040ec644SDavid van Moolenbroek  * its documentation for any purpose and without fee is hereby
42040ec644SDavid van Moolenbroek  * granted, provided that both the above copyright notice and this
43040ec644SDavid van Moolenbroek  * permission notice appear in all copies, that both the above
44040ec644SDavid van Moolenbroek  * copyright notice and this permission notice appear in all
45040ec644SDavid van Moolenbroek  * supporting documentation, and that the name of M.I.T. not be used
46040ec644SDavid van Moolenbroek  * in advertising or publicity pertaining to distribution of the
47040ec644SDavid van Moolenbroek  * software without specific, written prior permission.  M.I.T. makes
48040ec644SDavid van Moolenbroek  * no representations about the suitability of this software for any
49040ec644SDavid van Moolenbroek  * purpose.  It is provided "as is" without express or implied
50040ec644SDavid van Moolenbroek  * warranty.
51040ec644SDavid van Moolenbroek  *
52040ec644SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
53040ec644SDavid van Moolenbroek  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
54040ec644SDavid van Moolenbroek  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
55040ec644SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
56040ec644SDavid van Moolenbroek  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57040ec644SDavid van Moolenbroek  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
58040ec644SDavid van Moolenbroek  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
59040ec644SDavid van Moolenbroek  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
60040ec644SDavid van Moolenbroek  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
61040ec644SDavid van Moolenbroek  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
62040ec644SDavid van Moolenbroek  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63040ec644SDavid van Moolenbroek  * SUCH DAMAGE.
64040ec644SDavid van Moolenbroek  */
65040ec644SDavid van Moolenbroek 
66040ec644SDavid van Moolenbroek #if defined(__linux__) || defined(__MINT__)
67040ec644SDavid van Moolenbroek /* Keep this down to Linux or MiNT, it can create surprises elsewhere. */
68040ec644SDavid van Moolenbroek #define _GNU_SOURCE
69040ec644SDavid van Moolenbroek #endif
70040ec644SDavid van Moolenbroek 
71040ec644SDavid van Moolenbroek /* Needed for gmtime_r on Interix */
72040ec644SDavid van Moolenbroek #define _REENTRANT
73040ec644SDavid van Moolenbroek 
74040ec644SDavid van Moolenbroek #if HAVE_CONFIG_H
75040ec644SDavid van Moolenbroek #include "config.h"
76040ec644SDavid van Moolenbroek #endif
77040ec644SDavid van Moolenbroek #ifndef NETBSD
78040ec644SDavid van Moolenbroek #include <nbcompat.h>
79040ec644SDavid van Moolenbroek #endif
80040ec644SDavid van Moolenbroek 
81040ec644SDavid van Moolenbroek #include <sys/types.h>
82040ec644SDavid van Moolenbroek #include <sys/socket.h>
83040ec644SDavid van Moolenbroek 
84040ec644SDavid van Moolenbroek #include <ctype.h>
85040ec644SDavid van Moolenbroek #include <errno.h>
86040ec644SDavid van Moolenbroek #include <locale.h>
87040ec644SDavid van Moolenbroek #include <stdarg.h>
88040ec644SDavid van Moolenbroek #ifndef NETBSD
89040ec644SDavid van Moolenbroek #include <nbcompat/stdio.h>
90040ec644SDavid van Moolenbroek #else
91040ec644SDavid van Moolenbroek #include <stdio.h>
92040ec644SDavid van Moolenbroek #endif
93040ec644SDavid van Moolenbroek #include <stdlib.h>
94040ec644SDavid van Moolenbroek #include <string.h>
95040ec644SDavid van Moolenbroek #include <time.h>
96040ec644SDavid van Moolenbroek #include <unistd.h>
97040ec644SDavid van Moolenbroek 
98040ec644SDavid van Moolenbroek #include <netinet/in.h>
99040ec644SDavid van Moolenbroek #include <netinet/tcp.h>
100040ec644SDavid van Moolenbroek 
101040ec644SDavid van Moolenbroek #ifndef NETBSD
102040ec644SDavid van Moolenbroek #include <nbcompat/netdb.h>
103040ec644SDavid van Moolenbroek #else
104040ec644SDavid van Moolenbroek #include <netdb.h>
105040ec644SDavid van Moolenbroek #endif
106040ec644SDavid van Moolenbroek 
107040ec644SDavid van Moolenbroek #include <arpa/inet.h>
108040ec644SDavid van Moolenbroek 
109040ec644SDavid van Moolenbroek #include "fetch.h"
110040ec644SDavid van Moolenbroek #include "common.h"
111040ec644SDavid van Moolenbroek #include "httperr.h"
112040ec644SDavid van Moolenbroek 
113040ec644SDavid van Moolenbroek /* Maximum number of redirects to follow */
114040ec644SDavid van Moolenbroek #define MAX_REDIRECT 5
115040ec644SDavid van Moolenbroek 
116040ec644SDavid van Moolenbroek /* Symbolic names for reply codes we care about */
117040ec644SDavid van Moolenbroek #define HTTP_OK			200
118040ec644SDavid van Moolenbroek #define HTTP_PARTIAL		206
119040ec644SDavid van Moolenbroek #define HTTP_MOVED_PERM		301
120040ec644SDavid van Moolenbroek #define HTTP_MOVED_TEMP		302
121040ec644SDavid van Moolenbroek #define HTTP_SEE_OTHER		303
122040ec644SDavid van Moolenbroek #define HTTP_NOT_MODIFIED	304
123040ec644SDavid van Moolenbroek #define HTTP_TEMP_REDIRECT	307
124040ec644SDavid van Moolenbroek #define HTTP_NEED_AUTH		401
125040ec644SDavid van Moolenbroek #define HTTP_NEED_PROXY_AUTH	407
126040ec644SDavid van Moolenbroek #define HTTP_BAD_RANGE		416
127040ec644SDavid van Moolenbroek #define HTTP_PROTOCOL_ERROR	999
128040ec644SDavid van Moolenbroek 
129040ec644SDavid van Moolenbroek #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \
130040ec644SDavid van Moolenbroek 			    || (xyz) == HTTP_MOVED_TEMP \
131040ec644SDavid van Moolenbroek 			    || (xyz) == HTTP_TEMP_REDIRECT \
132040ec644SDavid van Moolenbroek 			    || (xyz) == HTTP_SEE_OTHER)
133040ec644SDavid van Moolenbroek 
134040ec644SDavid van Moolenbroek #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
135040ec644SDavid van Moolenbroek 
136040ec644SDavid van Moolenbroek 
137040ec644SDavid van Moolenbroek /*****************************************************************************
138040ec644SDavid van Moolenbroek  * I/O functions for decoding chunked streams
139040ec644SDavid van Moolenbroek  */
140040ec644SDavid van Moolenbroek 
141040ec644SDavid van Moolenbroek struct httpio
142040ec644SDavid van Moolenbroek {
143040ec644SDavid van Moolenbroek 	conn_t		*conn;		/* connection */
144040ec644SDavid van Moolenbroek 	int		 chunked;	/* chunked mode */
145040ec644SDavid van Moolenbroek 	int		 keep_alive;	/* keep-alive mode */
146040ec644SDavid van Moolenbroek 	char		*buf;		/* chunk buffer */
147040ec644SDavid van Moolenbroek 	size_t		 bufsize;	/* size of chunk buffer */
148040ec644SDavid van Moolenbroek 	ssize_t		 buflen;	/* amount of data currently in buffer */
149040ec644SDavid van Moolenbroek 	size_t		 bufpos;	/* current read offset in buffer */
150040ec644SDavid van Moolenbroek 	int		 eof;		/* end-of-file flag */
151040ec644SDavid van Moolenbroek 	int		 error;		/* error flag */
152040ec644SDavid van Moolenbroek 	size_t		 chunksize;	/* remaining size of current chunk */
153040ec644SDavid van Moolenbroek 	off_t		 contentlength;	/* remaining size of the content */
154040ec644SDavid van Moolenbroek };
155040ec644SDavid van Moolenbroek 
156040ec644SDavid van Moolenbroek /*
157040ec644SDavid van Moolenbroek  * Get next chunk header
158040ec644SDavid van Moolenbroek  */
159040ec644SDavid van Moolenbroek static ssize_t
http_new_chunk(struct httpio * io)160040ec644SDavid van Moolenbroek http_new_chunk(struct httpio *io)
161040ec644SDavid van Moolenbroek {
162040ec644SDavid van Moolenbroek 	char *p;
163040ec644SDavid van Moolenbroek 
164040ec644SDavid van Moolenbroek 	if (fetch_getln(io->conn) == -1)
165040ec644SDavid van Moolenbroek 		return (-1);
166040ec644SDavid van Moolenbroek 
167040ec644SDavid van Moolenbroek 	if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf))
168040ec644SDavid van Moolenbroek 		return (-1);
169040ec644SDavid van Moolenbroek 
170040ec644SDavid van Moolenbroek 	for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) {
171040ec644SDavid van Moolenbroek 		if (*p == ';')
172040ec644SDavid van Moolenbroek 			break;
173040ec644SDavid van Moolenbroek 		if (!isxdigit((unsigned char)*p))
174040ec644SDavid van Moolenbroek 			return (-1);
175040ec644SDavid van Moolenbroek 		if (isdigit((unsigned char)*p)) {
176040ec644SDavid van Moolenbroek 			io->chunksize = io->chunksize * 16 +
177040ec644SDavid van Moolenbroek 			    *p - '0';
178040ec644SDavid van Moolenbroek 		} else {
179040ec644SDavid van Moolenbroek 			io->chunksize = io->chunksize * 16 +
180040ec644SDavid van Moolenbroek 			    10 + tolower((unsigned char)*p) - 'a';
181040ec644SDavid van Moolenbroek 		}
182040ec644SDavid van Moolenbroek 	}
183040ec644SDavid van Moolenbroek 
184040ec644SDavid van Moolenbroek 	return (io->chunksize);
185040ec644SDavid van Moolenbroek }
186040ec644SDavid van Moolenbroek 
187040ec644SDavid van Moolenbroek /*
188040ec644SDavid van Moolenbroek  * Grow the input buffer to at least len bytes
189040ec644SDavid van Moolenbroek  */
190040ec644SDavid van Moolenbroek static int
http_growbuf(struct httpio * io,size_t len)191040ec644SDavid van Moolenbroek http_growbuf(struct httpio *io, size_t len)
192040ec644SDavid van Moolenbroek {
193040ec644SDavid van Moolenbroek 	char *tmp;
194040ec644SDavid van Moolenbroek 
195040ec644SDavid van Moolenbroek 	if (io->bufsize >= len)
196040ec644SDavid van Moolenbroek 		return (0);
197040ec644SDavid van Moolenbroek 
198040ec644SDavid van Moolenbroek 	if ((tmp = realloc(io->buf, len)) == NULL)
199040ec644SDavid van Moolenbroek 		return (-1);
200040ec644SDavid van Moolenbroek 	io->buf = tmp;
201040ec644SDavid van Moolenbroek 	io->bufsize = len;
202040ec644SDavid van Moolenbroek 	return (0);
203040ec644SDavid van Moolenbroek }
204040ec644SDavid van Moolenbroek 
205040ec644SDavid van Moolenbroek /*
206040ec644SDavid van Moolenbroek  * Fill the input buffer, do chunk decoding on the fly
207040ec644SDavid van Moolenbroek  */
208040ec644SDavid van Moolenbroek static ssize_t
http_fillbuf(struct httpio * io,size_t len)209040ec644SDavid van Moolenbroek http_fillbuf(struct httpio *io, size_t len)
210040ec644SDavid van Moolenbroek {
211040ec644SDavid van Moolenbroek 	if (io->error)
212040ec644SDavid van Moolenbroek 		return (-1);
213040ec644SDavid van Moolenbroek 	if (io->eof)
214040ec644SDavid van Moolenbroek 		return (0);
215040ec644SDavid van Moolenbroek 
216040ec644SDavid van Moolenbroek 	if (io->contentlength >= 0 && (off_t)len > io->contentlength)
217040ec644SDavid van Moolenbroek 		len = io->contentlength;
218040ec644SDavid van Moolenbroek 
219040ec644SDavid van Moolenbroek 	if (io->chunked == 0) {
220040ec644SDavid van Moolenbroek 		if (http_growbuf(io, len) == -1)
221040ec644SDavid van Moolenbroek 			return (-1);
222040ec644SDavid van Moolenbroek 		if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
223040ec644SDavid van Moolenbroek 			io->error = 1;
224040ec644SDavid van Moolenbroek 			return (-1);
225040ec644SDavid van Moolenbroek 		}
226040ec644SDavid van Moolenbroek 		if (io->contentlength)
227040ec644SDavid van Moolenbroek 			io->contentlength -= io->buflen;
228040ec644SDavid van Moolenbroek 		io->bufpos = 0;
229040ec644SDavid van Moolenbroek 		return (io->buflen);
230040ec644SDavid van Moolenbroek 	}
231040ec644SDavid van Moolenbroek 
232040ec644SDavid van Moolenbroek 	if (io->chunksize == 0) {
233040ec644SDavid van Moolenbroek 		switch (http_new_chunk(io)) {
234040ec644SDavid van Moolenbroek 		case -1:
235040ec644SDavid van Moolenbroek 			io->error = 1;
236040ec644SDavid van Moolenbroek 			return (-1);
237040ec644SDavid van Moolenbroek 		case 0:
238040ec644SDavid van Moolenbroek 			io->eof = 1;
239040ec644SDavid van Moolenbroek 			if (fetch_getln(io->conn) == -1)
240040ec644SDavid van Moolenbroek 				return (-1);
241040ec644SDavid van Moolenbroek 			return (0);
242040ec644SDavid van Moolenbroek 		}
243040ec644SDavid van Moolenbroek 	}
244040ec644SDavid van Moolenbroek 
245040ec644SDavid van Moolenbroek 	if (len > io->chunksize)
246040ec644SDavid van Moolenbroek 		len = io->chunksize;
247040ec644SDavid van Moolenbroek 	if (http_growbuf(io, len) == -1)
248040ec644SDavid van Moolenbroek 		return (-1);
249040ec644SDavid van Moolenbroek 	if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
250040ec644SDavid van Moolenbroek 		io->error = 1;
251040ec644SDavid van Moolenbroek 		return (-1);
252040ec644SDavid van Moolenbroek 	}
253040ec644SDavid van Moolenbroek 	io->chunksize -= io->buflen;
254040ec644SDavid van Moolenbroek 	if (io->contentlength >= 0)
255040ec644SDavid van Moolenbroek 		io->contentlength -= io->buflen;
256040ec644SDavid van Moolenbroek 
257040ec644SDavid van Moolenbroek 	if (io->chunksize == 0) {
258040ec644SDavid van Moolenbroek 		char endl[2];
259040ec644SDavid van Moolenbroek 		ssize_t len2;
260040ec644SDavid van Moolenbroek 
261040ec644SDavid van Moolenbroek 		len2 = fetch_read(io->conn, endl, 2);
262040ec644SDavid van Moolenbroek 		if (len2 == 1 && fetch_read(io->conn, endl + 1, 1) != 1)
263040ec644SDavid van Moolenbroek 			return (-1);
264040ec644SDavid van Moolenbroek 		if (len2 == -1 || endl[0] != '\r' || endl[1] != '\n')
265040ec644SDavid van Moolenbroek 			return (-1);
266040ec644SDavid van Moolenbroek 	}
267040ec644SDavid van Moolenbroek 
268040ec644SDavid van Moolenbroek 	io->bufpos = 0;
269040ec644SDavid van Moolenbroek 
270040ec644SDavid van Moolenbroek 	return (io->buflen);
271040ec644SDavid van Moolenbroek }
272040ec644SDavid van Moolenbroek 
273040ec644SDavid van Moolenbroek /*
274040ec644SDavid van Moolenbroek  * Read function
275040ec644SDavid van Moolenbroek  */
276040ec644SDavid van Moolenbroek static ssize_t
http_readfn(void * v,void * buf,size_t len)277040ec644SDavid van Moolenbroek http_readfn(void *v, void *buf, size_t len)
278040ec644SDavid van Moolenbroek {
279040ec644SDavid van Moolenbroek 	struct httpio *io = (struct httpio *)v;
280040ec644SDavid van Moolenbroek 	size_t l, pos;
281040ec644SDavid van Moolenbroek 
282040ec644SDavid van Moolenbroek 	if (io->error)
283040ec644SDavid van Moolenbroek 		return (-1);
284040ec644SDavid van Moolenbroek 	if (io->eof)
285040ec644SDavid van Moolenbroek 		return (0);
286040ec644SDavid van Moolenbroek 
287040ec644SDavid van Moolenbroek 	for (pos = 0; len > 0; pos += l, len -= l) {
288040ec644SDavid van Moolenbroek 		/* empty buffer */
289040ec644SDavid van Moolenbroek 		if (!io->buf || (ssize_t)io->bufpos == io->buflen)
290040ec644SDavid van Moolenbroek 			if (http_fillbuf(io, len) < 1)
291040ec644SDavid van Moolenbroek 				break;
292040ec644SDavid van Moolenbroek 		l = io->buflen - io->bufpos;
293040ec644SDavid van Moolenbroek 		if (len < l)
294040ec644SDavid van Moolenbroek 			l = len;
295040ec644SDavid van Moolenbroek 		memcpy((char *)buf + pos, io->buf + io->bufpos, l);
296040ec644SDavid van Moolenbroek 		io->bufpos += l;
297040ec644SDavid van Moolenbroek 	}
298040ec644SDavid van Moolenbroek 
299040ec644SDavid van Moolenbroek 	if (!pos && io->error)
300040ec644SDavid van Moolenbroek 		return (-1);
301040ec644SDavid van Moolenbroek 	return (pos);
302040ec644SDavid van Moolenbroek }
303040ec644SDavid van Moolenbroek 
304040ec644SDavid van Moolenbroek /*
305040ec644SDavid van Moolenbroek  * Write function
306040ec644SDavid van Moolenbroek  */
307040ec644SDavid van Moolenbroek static ssize_t
http_writefn(void * v,const void * buf,size_t len)308040ec644SDavid van Moolenbroek http_writefn(void *v, const void *buf, size_t len)
309040ec644SDavid van Moolenbroek {
310040ec644SDavid van Moolenbroek 	struct httpio *io = (struct httpio *)v;
311040ec644SDavid van Moolenbroek 
312040ec644SDavid van Moolenbroek 	return (fetch_write(io->conn, buf, len));
313040ec644SDavid van Moolenbroek }
314040ec644SDavid van Moolenbroek 
315040ec644SDavid van Moolenbroek /*
316040ec644SDavid van Moolenbroek  * Close function
317040ec644SDavid van Moolenbroek  */
318040ec644SDavid van Moolenbroek static void
http_closefn(void * v)319040ec644SDavid van Moolenbroek http_closefn(void *v)
320040ec644SDavid van Moolenbroek {
321040ec644SDavid van Moolenbroek 	struct httpio *io = (struct httpio *)v;
322040ec644SDavid van Moolenbroek 
323040ec644SDavid van Moolenbroek 	if (io->keep_alive) {
324040ec644SDavid van Moolenbroek 		int val;
325040ec644SDavid van Moolenbroek 
326040ec644SDavid van Moolenbroek 		val = 0;
327040ec644SDavid van Moolenbroek 		setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,
328040ec644SDavid van Moolenbroek 			   (socklen_t)sizeof(val));
329040ec644SDavid van Moolenbroek 			  fetch_cache_put(io->conn, fetch_close);
330040ec644SDavid van Moolenbroek #ifdef TCP_NOPUSH
331040ec644SDavid van Moolenbroek 		val = 1;
332040ec644SDavid van Moolenbroek 		setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,
333040ec644SDavid van Moolenbroek 		    sizeof(val));
334040ec644SDavid van Moolenbroek #endif
335040ec644SDavid van Moolenbroek 	} else {
336040ec644SDavid van Moolenbroek 		fetch_close(io->conn);
337040ec644SDavid van Moolenbroek 	}
338040ec644SDavid van Moolenbroek 
339040ec644SDavid van Moolenbroek 	free(io->buf);
340040ec644SDavid van Moolenbroek 	free(io);
341040ec644SDavid van Moolenbroek }
342040ec644SDavid van Moolenbroek 
343040ec644SDavid van Moolenbroek /*
344040ec644SDavid van Moolenbroek  * Wrap a file descriptor up
345040ec644SDavid van Moolenbroek  */
346040ec644SDavid van Moolenbroek static fetchIO *
http_funopen(conn_t * conn,int chunked,int keep_alive,off_t clength)347040ec644SDavid van Moolenbroek http_funopen(conn_t *conn, int chunked, int keep_alive, off_t clength)
348040ec644SDavid van Moolenbroek {
349040ec644SDavid van Moolenbroek 	struct httpio *io;
350040ec644SDavid van Moolenbroek 	fetchIO *f;
351040ec644SDavid van Moolenbroek 
352040ec644SDavid van Moolenbroek 	if ((io = calloc(1, sizeof(*io))) == NULL) {
353040ec644SDavid van Moolenbroek 		fetch_syserr();
354040ec644SDavid van Moolenbroek 		return (NULL);
355040ec644SDavid van Moolenbroek 	}
356040ec644SDavid van Moolenbroek 	io->conn = conn;
357040ec644SDavid van Moolenbroek 	io->chunked = chunked;
358040ec644SDavid van Moolenbroek 	io->contentlength = clength;
359040ec644SDavid van Moolenbroek 	io->keep_alive = keep_alive;
360040ec644SDavid van Moolenbroek 	f = fetchIO_unopen(io, http_readfn, http_writefn, http_closefn);
361040ec644SDavid van Moolenbroek 	if (f == NULL) {
362040ec644SDavid van Moolenbroek 		fetch_syserr();
363040ec644SDavid van Moolenbroek 		free(io);
364040ec644SDavid van Moolenbroek 		return (NULL);
365040ec644SDavid van Moolenbroek 	}
366040ec644SDavid van Moolenbroek 	return (f);
367040ec644SDavid van Moolenbroek }
368040ec644SDavid van Moolenbroek 
369040ec644SDavid van Moolenbroek 
370040ec644SDavid van Moolenbroek /*****************************************************************************
371040ec644SDavid van Moolenbroek  * Helper functions for talking to the server and parsing its replies
372040ec644SDavid van Moolenbroek  */
373040ec644SDavid van Moolenbroek 
374040ec644SDavid van Moolenbroek /* Header types */
375040ec644SDavid van Moolenbroek typedef enum {
376040ec644SDavid van Moolenbroek 	hdr_syserror = -2,
377040ec644SDavid van Moolenbroek 	hdr_error = -1,
378040ec644SDavid van Moolenbroek 	hdr_end = 0,
379040ec644SDavid van Moolenbroek 	hdr_unknown = 1,
380040ec644SDavid van Moolenbroek 	hdr_connection,
381040ec644SDavid van Moolenbroek 	hdr_content_length,
382040ec644SDavid van Moolenbroek 	hdr_content_range,
383040ec644SDavid van Moolenbroek 	hdr_last_modified,
384040ec644SDavid van Moolenbroek 	hdr_location,
385040ec644SDavid van Moolenbroek 	hdr_transfer_encoding,
386040ec644SDavid van Moolenbroek 	hdr_www_authenticate
387040ec644SDavid van Moolenbroek } hdr_t;
388040ec644SDavid van Moolenbroek 
389040ec644SDavid van Moolenbroek /* Names of interesting headers */
390040ec644SDavid van Moolenbroek static struct {
391040ec644SDavid van Moolenbroek 	hdr_t		 num;
392040ec644SDavid van Moolenbroek 	const char	*name;
393040ec644SDavid van Moolenbroek } hdr_names[] = {
394040ec644SDavid van Moolenbroek 	{ hdr_connection,		"Connection" },
395040ec644SDavid van Moolenbroek 	{ hdr_content_length,		"Content-Length" },
396040ec644SDavid van Moolenbroek 	{ hdr_content_range,		"Content-Range" },
397040ec644SDavid van Moolenbroek 	{ hdr_last_modified,		"Last-Modified" },
398040ec644SDavid van Moolenbroek 	{ hdr_location,			"Location" },
399040ec644SDavid van Moolenbroek 	{ hdr_transfer_encoding,	"Transfer-Encoding" },
400040ec644SDavid van Moolenbroek 	{ hdr_www_authenticate,		"WWW-Authenticate" },
401040ec644SDavid van Moolenbroek 	{ hdr_unknown,			NULL },
402040ec644SDavid van Moolenbroek };
403040ec644SDavid van Moolenbroek 
404040ec644SDavid van Moolenbroek /*
405040ec644SDavid van Moolenbroek  * Send a formatted line; optionally echo to terminal
406040ec644SDavid van Moolenbroek  */
407*0a6a1f1dSLionel Sambuc __printflike(2, 3)
408040ec644SDavid van Moolenbroek static int
http_cmd(conn_t * conn,const char * fmt,...)409040ec644SDavid van Moolenbroek http_cmd(conn_t *conn, const char *fmt, ...)
410040ec644SDavid van Moolenbroek {
411040ec644SDavid van Moolenbroek 	va_list ap;
412040ec644SDavid van Moolenbroek 	size_t len;
413040ec644SDavid van Moolenbroek 	char *msg;
414040ec644SDavid van Moolenbroek 	ssize_t r;
415040ec644SDavid van Moolenbroek 
416040ec644SDavid van Moolenbroek 	va_start(ap, fmt);
417040ec644SDavid van Moolenbroek 	len = vasprintf(&msg, fmt, ap);
418040ec644SDavid van Moolenbroek 	va_end(ap);
419040ec644SDavid van Moolenbroek 
420040ec644SDavid van Moolenbroek 	if (msg == NULL) {
421040ec644SDavid van Moolenbroek 		errno = ENOMEM;
422040ec644SDavid van Moolenbroek 		fetch_syserr();
423040ec644SDavid van Moolenbroek 		return (-1);
424040ec644SDavid van Moolenbroek 	}
425040ec644SDavid van Moolenbroek 
426040ec644SDavid van Moolenbroek 	r = fetch_write(conn, msg, len);
427040ec644SDavid van Moolenbroek 	free(msg);
428040ec644SDavid van Moolenbroek 
429040ec644SDavid van Moolenbroek 	if (r == -1) {
430040ec644SDavid van Moolenbroek 		fetch_syserr();
431040ec644SDavid van Moolenbroek 		return (-1);
432040ec644SDavid van Moolenbroek 	}
433040ec644SDavid van Moolenbroek 
434040ec644SDavid van Moolenbroek 	return (0);
435040ec644SDavid van Moolenbroek }
436040ec644SDavid van Moolenbroek 
437040ec644SDavid van Moolenbroek /*
438040ec644SDavid van Moolenbroek  * Get and parse status line
439040ec644SDavid van Moolenbroek  */
440040ec644SDavid van Moolenbroek static int
http_get_reply(conn_t * conn)441040ec644SDavid van Moolenbroek http_get_reply(conn_t *conn)
442040ec644SDavid van Moolenbroek {
443040ec644SDavid van Moolenbroek 	char *p;
444040ec644SDavid van Moolenbroek 
445040ec644SDavid van Moolenbroek 	if (fetch_getln(conn) == -1)
446040ec644SDavid van Moolenbroek 		return (-1);
447040ec644SDavid van Moolenbroek 	/*
448040ec644SDavid van Moolenbroek 	 * A valid status line looks like "HTTP/m.n xyz reason" where m
449040ec644SDavid van Moolenbroek 	 * and n are the major and minor protocol version numbers and xyz
450040ec644SDavid van Moolenbroek 	 * is the reply code.
451040ec644SDavid van Moolenbroek 	 * Unfortunately, there are servers out there (NCSA 1.5.1, to name
452040ec644SDavid van Moolenbroek 	 * just one) that do not send a version number, so we can't rely
453040ec644SDavid van Moolenbroek 	 * on finding one, but if we do, insist on it being 1.0 or 1.1.
454040ec644SDavid van Moolenbroek 	 * We don't care about the reason phrase.
455040ec644SDavid van Moolenbroek 	 */
456040ec644SDavid van Moolenbroek 	if (strncmp(conn->buf, "HTTP", 4) != 0)
457040ec644SDavid van Moolenbroek 		return (HTTP_PROTOCOL_ERROR);
458040ec644SDavid van Moolenbroek 	p = conn->buf + 4;
459040ec644SDavid van Moolenbroek 	if (*p == '/') {
460040ec644SDavid van Moolenbroek 		if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1'))
461040ec644SDavid van Moolenbroek 			return (HTTP_PROTOCOL_ERROR);
462040ec644SDavid van Moolenbroek 		p += 4;
463040ec644SDavid van Moolenbroek 	}
464040ec644SDavid van Moolenbroek 	if (*p != ' ' ||
465040ec644SDavid van Moolenbroek 	    !isdigit((unsigned char)p[1]) ||
466040ec644SDavid van Moolenbroek 	    !isdigit((unsigned char)p[2]) ||
467040ec644SDavid van Moolenbroek 	    !isdigit((unsigned char)p[3]))
468040ec644SDavid van Moolenbroek 		return (HTTP_PROTOCOL_ERROR);
469040ec644SDavid van Moolenbroek 
470040ec644SDavid van Moolenbroek 	conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0');
471040ec644SDavid van Moolenbroek 	return (conn->err);
472040ec644SDavid van Moolenbroek }
473040ec644SDavid van Moolenbroek 
474040ec644SDavid van Moolenbroek /*
475040ec644SDavid van Moolenbroek  * Check a header; if the type matches the given string, return a pointer
476040ec644SDavid van Moolenbroek  * to the beginning of the value.
477040ec644SDavid van Moolenbroek  */
478040ec644SDavid van Moolenbroek static const char *
http_match(const char * str,const char * hdr)479040ec644SDavid van Moolenbroek http_match(const char *str, const char *hdr)
480040ec644SDavid van Moolenbroek {
481040ec644SDavid van Moolenbroek 	while (*str && *hdr &&
482040ec644SDavid van Moolenbroek 	    tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++))
483040ec644SDavid van Moolenbroek 		/* nothing */;
484040ec644SDavid van Moolenbroek 	if (*str || *hdr != ':')
485040ec644SDavid van Moolenbroek 		return (NULL);
486040ec644SDavid van Moolenbroek 	while (*hdr && isspace((unsigned char)*++hdr))
487040ec644SDavid van Moolenbroek 		/* nothing */;
488040ec644SDavid van Moolenbroek 	return (hdr);
489040ec644SDavid van Moolenbroek }
490040ec644SDavid van Moolenbroek 
491040ec644SDavid van Moolenbroek /*
492040ec644SDavid van Moolenbroek  * Get the next header and return the appropriate symbolic code.
493040ec644SDavid van Moolenbroek  */
494040ec644SDavid van Moolenbroek static hdr_t
http_next_header(conn_t * conn,const char ** p)495040ec644SDavid van Moolenbroek http_next_header(conn_t *conn, const char **p)
496040ec644SDavid van Moolenbroek {
497040ec644SDavid van Moolenbroek 	int i;
498040ec644SDavid van Moolenbroek 
499040ec644SDavid van Moolenbroek 	if (fetch_getln(conn) == -1)
500040ec644SDavid van Moolenbroek 		return (hdr_syserror);
501040ec644SDavid van Moolenbroek 	while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1]))
502040ec644SDavid van Moolenbroek 		conn->buflen--;
503040ec644SDavid van Moolenbroek 	conn->buf[conn->buflen] = '\0';
504040ec644SDavid van Moolenbroek 	if (conn->buflen == 0)
505040ec644SDavid van Moolenbroek 		return (hdr_end);
506040ec644SDavid van Moolenbroek 	/*
507040ec644SDavid van Moolenbroek 	 * We could check for malformed headers but we don't really care.
508040ec644SDavid van Moolenbroek 	 * A valid header starts with a token immediately followed by a
509040ec644SDavid van Moolenbroek 	 * colon; a token is any sequence of non-control, non-whitespace
510040ec644SDavid van Moolenbroek 	 * characters except "()<>@,;:\\\"{}".
511040ec644SDavid van Moolenbroek 	 */
512040ec644SDavid van Moolenbroek 	for (i = 0; hdr_names[i].num != hdr_unknown; i++)
513040ec644SDavid van Moolenbroek 		if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL)
514040ec644SDavid van Moolenbroek 			return (hdr_names[i].num);
515040ec644SDavid van Moolenbroek 	return (hdr_unknown);
516040ec644SDavid van Moolenbroek }
517040ec644SDavid van Moolenbroek 
518040ec644SDavid van Moolenbroek /*
519040ec644SDavid van Moolenbroek  * Parse a last-modified header
520040ec644SDavid van Moolenbroek  */
521040ec644SDavid van Moolenbroek static int
http_parse_mtime(const char * p,time_t * mtime)522040ec644SDavid van Moolenbroek http_parse_mtime(const char *p, time_t *mtime)
523040ec644SDavid van Moolenbroek {
524040ec644SDavid van Moolenbroek 	char locale[64], *r;
525040ec644SDavid van Moolenbroek 	struct tm tm;
526040ec644SDavid van Moolenbroek 
527040ec644SDavid van Moolenbroek 	strncpy(locale, setlocale(LC_TIME, NULL), sizeof(locale));
528040ec644SDavid van Moolenbroek 	setlocale(LC_TIME, "C");
529040ec644SDavid van Moolenbroek 	r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
530040ec644SDavid van Moolenbroek 	/* XXX should add support for date-2 and date-3 */
531040ec644SDavid van Moolenbroek 	setlocale(LC_TIME, locale);
532040ec644SDavid van Moolenbroek 	if (r == NULL)
533040ec644SDavid van Moolenbroek 		return (-1);
534040ec644SDavid van Moolenbroek 	*mtime = timegm(&tm);
535040ec644SDavid van Moolenbroek 	return (0);
536040ec644SDavid van Moolenbroek }
537040ec644SDavid van Moolenbroek 
538040ec644SDavid van Moolenbroek /*
539040ec644SDavid van Moolenbroek  * Parse a content-length header
540040ec644SDavid van Moolenbroek  */
541040ec644SDavid van Moolenbroek static int
http_parse_length(const char * p,off_t * length)542040ec644SDavid van Moolenbroek http_parse_length(const char *p, off_t *length)
543040ec644SDavid van Moolenbroek {
544040ec644SDavid van Moolenbroek 	off_t len;
545040ec644SDavid van Moolenbroek 
546040ec644SDavid van Moolenbroek 	for (len = 0; *p && isdigit((unsigned char)*p); ++p)
547040ec644SDavid van Moolenbroek 		len = len * 10 + (*p - '0');
548040ec644SDavid van Moolenbroek 	if (*p)
549040ec644SDavid van Moolenbroek 		return (-1);
550040ec644SDavid van Moolenbroek 	*length = len;
551040ec644SDavid van Moolenbroek 	return (0);
552040ec644SDavid van Moolenbroek }
553040ec644SDavid van Moolenbroek 
554040ec644SDavid van Moolenbroek /*
555040ec644SDavid van Moolenbroek  * Parse a content-range header
556040ec644SDavid van Moolenbroek  */
557040ec644SDavid van Moolenbroek static int
http_parse_range(const char * p,off_t * offset,off_t * length,off_t * size)558040ec644SDavid van Moolenbroek http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
559040ec644SDavid van Moolenbroek {
560040ec644SDavid van Moolenbroek 	off_t first, last, len;
561040ec644SDavid van Moolenbroek 
562040ec644SDavid van Moolenbroek 	if (strncasecmp(p, "bytes ", 6) != 0)
563040ec644SDavid van Moolenbroek 		return (-1);
564040ec644SDavid van Moolenbroek 	p += 6;
565040ec644SDavid van Moolenbroek 	if (*p == '*') {
566040ec644SDavid van Moolenbroek 		first = last = -1;
567040ec644SDavid van Moolenbroek 		++p;
568040ec644SDavid van Moolenbroek 	} else {
569040ec644SDavid van Moolenbroek 		for (first = 0; *p && isdigit((unsigned char)*p); ++p)
570040ec644SDavid van Moolenbroek 			first = first * 10 + *p - '0';
571040ec644SDavid van Moolenbroek 		if (*p != '-')
572040ec644SDavid van Moolenbroek 			return (-1);
573040ec644SDavid van Moolenbroek 		for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
574040ec644SDavid van Moolenbroek 			last = last * 10 + *p - '0';
575040ec644SDavid van Moolenbroek 	}
576040ec644SDavid van Moolenbroek 	if (first > last || *p != '/')
577040ec644SDavid van Moolenbroek 		return (-1);
578040ec644SDavid van Moolenbroek 	for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
579040ec644SDavid van Moolenbroek 		len = len * 10 + *p - '0';
580040ec644SDavid van Moolenbroek 	if (*p || len < last - first + 1)
581040ec644SDavid van Moolenbroek 		return (-1);
582040ec644SDavid van Moolenbroek 	if (first == -1)
583040ec644SDavid van Moolenbroek 		*length = 0;
584040ec644SDavid van Moolenbroek 	else
585040ec644SDavid van Moolenbroek 		*length = last - first + 1;
586040ec644SDavid van Moolenbroek 	*offset = first;
587040ec644SDavid van Moolenbroek 	*size = len;
588040ec644SDavid van Moolenbroek 	return (0);
589040ec644SDavid van Moolenbroek }
590040ec644SDavid van Moolenbroek 
591040ec644SDavid van Moolenbroek 
592040ec644SDavid van Moolenbroek /*****************************************************************************
593040ec644SDavid van Moolenbroek  * Helper functions for authorization
594040ec644SDavid van Moolenbroek  */
595040ec644SDavid van Moolenbroek 
596040ec644SDavid van Moolenbroek /*
597040ec644SDavid van Moolenbroek  * Base64 encoding
598040ec644SDavid van Moolenbroek  */
599040ec644SDavid van Moolenbroek static char *
http_base64(const char * src)600040ec644SDavid van Moolenbroek http_base64(const char *src)
601040ec644SDavid van Moolenbroek {
602040ec644SDavid van Moolenbroek 	static const char base64[] =
603040ec644SDavid van Moolenbroek 	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
604040ec644SDavid van Moolenbroek 	    "abcdefghijklmnopqrstuvwxyz"
605040ec644SDavid van Moolenbroek 	    "0123456789+/";
606040ec644SDavid van Moolenbroek 	char *str, *dst;
607040ec644SDavid van Moolenbroek 	size_t l;
608040ec644SDavid van Moolenbroek 	unsigned int t, r;
609040ec644SDavid van Moolenbroek 
610040ec644SDavid van Moolenbroek 	l = strlen(src);
611040ec644SDavid van Moolenbroek 	if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL)
612040ec644SDavid van Moolenbroek 		return (NULL);
613040ec644SDavid van Moolenbroek 	dst = str;
614040ec644SDavid van Moolenbroek 	r = 0;
615040ec644SDavid van Moolenbroek 
616040ec644SDavid van Moolenbroek 	while (l >= 3) {
617040ec644SDavid van Moolenbroek 		t = (src[0] << 16) | (src[1] << 8) | src[2];
618040ec644SDavid van Moolenbroek 		dst[0] = base64[(t >> 18) & 0x3f];
619040ec644SDavid van Moolenbroek 		dst[1] = base64[(t >> 12) & 0x3f];
620040ec644SDavid van Moolenbroek 		dst[2] = base64[(t >> 6) & 0x3f];
621040ec644SDavid van Moolenbroek 		dst[3] = base64[(t >> 0) & 0x3f];
622040ec644SDavid van Moolenbroek 		src += 3; l -= 3;
623040ec644SDavid van Moolenbroek 		dst += 4; r += 4;
624040ec644SDavid van Moolenbroek 	}
625040ec644SDavid van Moolenbroek 
626040ec644SDavid van Moolenbroek 	switch (l) {
627040ec644SDavid van Moolenbroek 	case 2:
628040ec644SDavid van Moolenbroek 		t = (src[0] << 16) | (src[1] << 8);
629040ec644SDavid van Moolenbroek 		dst[0] = base64[(t >> 18) & 0x3f];
630040ec644SDavid van Moolenbroek 		dst[1] = base64[(t >> 12) & 0x3f];
631040ec644SDavid van Moolenbroek 		dst[2] = base64[(t >> 6) & 0x3f];
632040ec644SDavid van Moolenbroek 		dst[3] = '=';
633040ec644SDavid van Moolenbroek 		dst += 4;
634040ec644SDavid van Moolenbroek 		r += 4;
635040ec644SDavid van Moolenbroek 		break;
636040ec644SDavid van Moolenbroek 	case 1:
637040ec644SDavid van Moolenbroek 		t = src[0] << 16;
638040ec644SDavid van Moolenbroek 		dst[0] = base64[(t >> 18) & 0x3f];
639040ec644SDavid van Moolenbroek 		dst[1] = base64[(t >> 12) & 0x3f];
640040ec644SDavid van Moolenbroek 		dst[2] = dst[3] = '=';
641040ec644SDavid van Moolenbroek 		dst += 4;
642040ec644SDavid van Moolenbroek 		r += 4;
643040ec644SDavid van Moolenbroek 		break;
644040ec644SDavid van Moolenbroek 	case 0:
645040ec644SDavid van Moolenbroek 		break;
646040ec644SDavid van Moolenbroek 	}
647040ec644SDavid van Moolenbroek 
648040ec644SDavid van Moolenbroek 	*dst = 0;
649040ec644SDavid van Moolenbroek 	return (str);
650040ec644SDavid van Moolenbroek }
651040ec644SDavid van Moolenbroek 
652040ec644SDavid van Moolenbroek /*
653040ec644SDavid van Moolenbroek  * Encode username and password
654040ec644SDavid van Moolenbroek  */
655040ec644SDavid van Moolenbroek static int
http_basic_auth(conn_t * conn,const char * hdr,const char * usr,const char * pwd)656040ec644SDavid van Moolenbroek http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
657040ec644SDavid van Moolenbroek {
658040ec644SDavid van Moolenbroek 	char *upw, *auth;
659040ec644SDavid van Moolenbroek 	int r;
660040ec644SDavid van Moolenbroek 
661040ec644SDavid van Moolenbroek 	if (asprintf(&upw, "%s:%s", usr, pwd) == -1)
662040ec644SDavid van Moolenbroek 		return (-1);
663040ec644SDavid van Moolenbroek 	auth = http_base64(upw);
664040ec644SDavid van Moolenbroek 	free(upw);
665040ec644SDavid van Moolenbroek 	if (auth == NULL)
666040ec644SDavid van Moolenbroek 		return (-1);
667040ec644SDavid van Moolenbroek 	r = http_cmd(conn, "%s: Basic %s\r\n", hdr, auth);
668040ec644SDavid van Moolenbroek 	free(auth);
669040ec644SDavid van Moolenbroek 	return (r);
670040ec644SDavid van Moolenbroek }
671040ec644SDavid van Moolenbroek 
672040ec644SDavid van Moolenbroek /*
673040ec644SDavid van Moolenbroek  * Send an authorization header
674040ec644SDavid van Moolenbroek  */
675040ec644SDavid van Moolenbroek static int
http_authorize(conn_t * conn,const char * hdr,const char * p)676040ec644SDavid van Moolenbroek http_authorize(conn_t *conn, const char *hdr, const char *p)
677040ec644SDavid van Moolenbroek {
678040ec644SDavid van Moolenbroek 	/* basic authorization */
679040ec644SDavid van Moolenbroek 	if (strncasecmp(p, "basic:", 6) == 0) {
680040ec644SDavid van Moolenbroek 		char *user, *pwd, *str;
681040ec644SDavid van Moolenbroek 		int r;
682040ec644SDavid van Moolenbroek 
683040ec644SDavid van Moolenbroek 		/* skip realm */
684040ec644SDavid van Moolenbroek 		for (p += 6; *p && *p != ':'; ++p)
685040ec644SDavid van Moolenbroek 			/* nothing */ ;
686040ec644SDavid van Moolenbroek 		if (!*p || strchr(++p, ':') == NULL)
687040ec644SDavid van Moolenbroek 			return (-1);
688040ec644SDavid van Moolenbroek 		if ((str = strdup(p)) == NULL)
689040ec644SDavid van Moolenbroek 			return (-1); /* XXX */
690040ec644SDavid van Moolenbroek 		user = str;
691040ec644SDavid van Moolenbroek 		pwd = strchr(str, ':');
692040ec644SDavid van Moolenbroek 		*pwd++ = '\0';
693040ec644SDavid van Moolenbroek 		r = http_basic_auth(conn, hdr, user, pwd);
694040ec644SDavid van Moolenbroek 		free(str);
695040ec644SDavid van Moolenbroek 		return (r);
696040ec644SDavid van Moolenbroek 	}
697040ec644SDavid van Moolenbroek 	return (-1);
698040ec644SDavid van Moolenbroek }
699040ec644SDavid van Moolenbroek 
700040ec644SDavid van Moolenbroek 
701040ec644SDavid van Moolenbroek /*****************************************************************************
702040ec644SDavid van Moolenbroek  * Helper functions for connecting to a server or proxy
703040ec644SDavid van Moolenbroek  */
704040ec644SDavid van Moolenbroek 
705040ec644SDavid van Moolenbroek /*
706040ec644SDavid van Moolenbroek  * Connect to the correct HTTP server or proxy.
707040ec644SDavid van Moolenbroek  */
708040ec644SDavid van Moolenbroek static conn_t *
http_connect(struct url * URL,struct url * purl,const char * flags,int * cached)709040ec644SDavid van Moolenbroek http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
710040ec644SDavid van Moolenbroek {
711040ec644SDavid van Moolenbroek 	conn_t *conn;
712040ec644SDavid van Moolenbroek 	int af, verbose;
713040ec644SDavid van Moolenbroek #ifdef TCP_NOPUSH
714040ec644SDavid van Moolenbroek 	int val;
715040ec644SDavid van Moolenbroek #endif
716040ec644SDavid van Moolenbroek 
717040ec644SDavid van Moolenbroek 	*cached = 1;
718040ec644SDavid van Moolenbroek 
719040ec644SDavid van Moolenbroek #ifdef INET6
720040ec644SDavid van Moolenbroek 	af = AF_UNSPEC;
721040ec644SDavid van Moolenbroek #else
722040ec644SDavid van Moolenbroek 	af = AF_INET;
723040ec644SDavid van Moolenbroek #endif
724040ec644SDavid van Moolenbroek 
725040ec644SDavid van Moolenbroek 	verbose = CHECK_FLAG('v');
726040ec644SDavid van Moolenbroek 	if (CHECK_FLAG('4'))
727040ec644SDavid van Moolenbroek 		af = AF_INET;
728040ec644SDavid van Moolenbroek #ifdef INET6
729040ec644SDavid van Moolenbroek 	else if (CHECK_FLAG('6'))
730040ec644SDavid van Moolenbroek 		af = AF_INET6;
731040ec644SDavid van Moolenbroek #endif
732040ec644SDavid van Moolenbroek 
733040ec644SDavid van Moolenbroek 	if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) {
734040ec644SDavid van Moolenbroek 		URL = purl;
735040ec644SDavid van Moolenbroek 	} else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
736040ec644SDavid van Moolenbroek 		/* can't talk http to an ftp server */
737040ec644SDavid van Moolenbroek 		/* XXX should set an error code */
738040ec644SDavid van Moolenbroek 		return (NULL);
739040ec644SDavid van Moolenbroek 	}
740040ec644SDavid van Moolenbroek 
741040ec644SDavid van Moolenbroek 	if ((conn = fetch_cache_get(URL, af)) != NULL) {
742040ec644SDavid van Moolenbroek 		*cached = 1;
743040ec644SDavid van Moolenbroek 		return (conn);
744040ec644SDavid van Moolenbroek 	}
745040ec644SDavid van Moolenbroek 
746040ec644SDavid van Moolenbroek 	if ((conn = fetch_connect(URL, af, verbose)) == NULL)
747040ec644SDavid van Moolenbroek 		/* fetch_connect() has already set an error code */
748040ec644SDavid van Moolenbroek 		return (NULL);
749040ec644SDavid van Moolenbroek 	if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 &&
750040ec644SDavid van Moolenbroek 	    fetch_ssl(conn, verbose) == -1) {
751040ec644SDavid van Moolenbroek 		fetch_close(conn);
752040ec644SDavid van Moolenbroek 		/* grrr */
753040ec644SDavid van Moolenbroek #ifdef EAUTH
754040ec644SDavid van Moolenbroek 		errno = EAUTH;
755040ec644SDavid van Moolenbroek #else
756040ec644SDavid van Moolenbroek 		errno = EPERM;
757040ec644SDavid van Moolenbroek #endif
758040ec644SDavid van Moolenbroek 		fetch_syserr();
759040ec644SDavid van Moolenbroek 		return (NULL);
760040ec644SDavid van Moolenbroek 	}
761040ec644SDavid van Moolenbroek 
762040ec644SDavid van Moolenbroek #ifdef TCP_NOPUSH
763040ec644SDavid van Moolenbroek 	val = 1;
764040ec644SDavid van Moolenbroek 	setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val));
765040ec644SDavid van Moolenbroek #endif
766040ec644SDavid van Moolenbroek 
767040ec644SDavid van Moolenbroek 	return (conn);
768040ec644SDavid van Moolenbroek }
769040ec644SDavid van Moolenbroek 
770040ec644SDavid van Moolenbroek static struct url *
http_get_proxy(struct url * url,const char * flags)771040ec644SDavid van Moolenbroek http_get_proxy(struct url * url, const char *flags)
772040ec644SDavid van Moolenbroek {
773040ec644SDavid van Moolenbroek 	struct url *purl;
774040ec644SDavid van Moolenbroek 	char *p;
775040ec644SDavid van Moolenbroek 
776040ec644SDavid van Moolenbroek 	if (flags != NULL && strchr(flags, 'd') != NULL)
777040ec644SDavid van Moolenbroek 		return (NULL);
778040ec644SDavid van Moolenbroek 	if (fetch_no_proxy_match(url->host))
779040ec644SDavid van Moolenbroek 		return (NULL);
780040ec644SDavid van Moolenbroek 	if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
781040ec644SDavid van Moolenbroek 	    *p && (purl = fetchParseURL(p))) {
782040ec644SDavid van Moolenbroek 		if (!*purl->scheme)
783040ec644SDavid van Moolenbroek 			strcpy(purl->scheme, SCHEME_HTTP);
784040ec644SDavid van Moolenbroek 		if (!purl->port)
785040ec644SDavid van Moolenbroek 			purl->port = fetch_default_proxy_port(purl->scheme);
786040ec644SDavid van Moolenbroek 		if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
787040ec644SDavid van Moolenbroek 			return (purl);
788040ec644SDavid van Moolenbroek 		fetchFreeURL(purl);
789040ec644SDavid van Moolenbroek 	}
790040ec644SDavid van Moolenbroek 	return (NULL);
791040ec644SDavid van Moolenbroek }
792040ec644SDavid van Moolenbroek 
793040ec644SDavid van Moolenbroek static void
set_if_modified_since(conn_t * conn,time_t last_modified)794040ec644SDavid van Moolenbroek set_if_modified_since(conn_t *conn, time_t last_modified)
795040ec644SDavid van Moolenbroek {
796040ec644SDavid van Moolenbroek 	static const char weekdays[] = "SunMonTueWedThuFriSat";
797040ec644SDavid van Moolenbroek 	static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
798040ec644SDavid van Moolenbroek 	struct tm tm;
799040ec644SDavid van Moolenbroek 	char buf[80];
800040ec644SDavid van Moolenbroek 	gmtime_r(&last_modified, &tm);
801040ec644SDavid van Moolenbroek 	snprintf(buf, sizeof(buf), "%.3s, %02d %.3s %4d %02d:%02d:%02d GMT",
802040ec644SDavid van Moolenbroek 	    weekdays + tm.tm_wday * 3, tm.tm_mday, months + tm.tm_mon * 3,
803040ec644SDavid van Moolenbroek 	    tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
804040ec644SDavid van Moolenbroek 	http_cmd(conn, "If-Modified-Since: %s\r\n", buf);
805040ec644SDavid van Moolenbroek }
806040ec644SDavid van Moolenbroek 
807040ec644SDavid van Moolenbroek 
808040ec644SDavid van Moolenbroek /*****************************************************************************
809040ec644SDavid van Moolenbroek  * Core
810040ec644SDavid van Moolenbroek  */
811040ec644SDavid van Moolenbroek 
812040ec644SDavid van Moolenbroek /*
813040ec644SDavid van Moolenbroek  * Send a request and process the reply
814040ec644SDavid van Moolenbroek  *
815040ec644SDavid van Moolenbroek  * XXX This function is way too long, the do..while loop should be split
816040ec644SDavid van Moolenbroek  * XXX off into a separate function.
817040ec644SDavid van Moolenbroek  */
818040ec644SDavid van Moolenbroek fetchIO *
http_request(struct url * URL,const char * op,struct url_stat * us,struct url * purl,const char * flags)819040ec644SDavid van Moolenbroek http_request(struct url *URL, const char *op, struct url_stat *us,
820040ec644SDavid van Moolenbroek     struct url *purl, const char *flags)
821040ec644SDavid van Moolenbroek {
822040ec644SDavid van Moolenbroek 	conn_t *conn;
823040ec644SDavid van Moolenbroek 	struct url *url, *new;
824040ec644SDavid van Moolenbroek 	int chunked, direct, if_modified_since, need_auth, noredirect;
825040ec644SDavid van Moolenbroek 	int keep_alive, verbose, cached;
826040ec644SDavid van Moolenbroek 	int e, i, n, val;
827040ec644SDavid van Moolenbroek 	off_t offset, clength, length, size;
828040ec644SDavid van Moolenbroek 	time_t mtime;
829040ec644SDavid van Moolenbroek 	const char *p;
830040ec644SDavid van Moolenbroek 	fetchIO *f;
831040ec644SDavid van Moolenbroek 	hdr_t h;
832040ec644SDavid van Moolenbroek 	char hbuf[URL_HOSTLEN + 7], *host;
833040ec644SDavid van Moolenbroek 
834040ec644SDavid van Moolenbroek 	direct = CHECK_FLAG('d');
835040ec644SDavid van Moolenbroek 	noredirect = CHECK_FLAG('A');
836040ec644SDavid van Moolenbroek 	verbose = CHECK_FLAG('v');
837040ec644SDavid van Moolenbroek 	if_modified_since = CHECK_FLAG('i');
838040ec644SDavid van Moolenbroek 	keep_alive = 0;
839040ec644SDavid van Moolenbroek 
840040ec644SDavid van Moolenbroek 	if (direct && purl) {
841040ec644SDavid van Moolenbroek 		fetchFreeURL(purl);
842040ec644SDavid van Moolenbroek 		purl = NULL;
843040ec644SDavid van Moolenbroek 	}
844040ec644SDavid van Moolenbroek 
845040ec644SDavid van Moolenbroek 	/* try the provided URL first */
846040ec644SDavid van Moolenbroek 	url = URL;
847040ec644SDavid van Moolenbroek 
848040ec644SDavid van Moolenbroek 	/* if the A flag is set, we only get one try */
849040ec644SDavid van Moolenbroek 	n = noredirect ? 1 : MAX_REDIRECT;
850040ec644SDavid van Moolenbroek 	i = 0;
851040ec644SDavid van Moolenbroek 
852040ec644SDavid van Moolenbroek 	e = HTTP_PROTOCOL_ERROR;
853040ec644SDavid van Moolenbroek 	need_auth = 0;
854040ec644SDavid van Moolenbroek 	do {
855040ec644SDavid van Moolenbroek 		new = NULL;
856040ec644SDavid van Moolenbroek 		chunked = 0;
857040ec644SDavid van Moolenbroek 		offset = 0;
858040ec644SDavid van Moolenbroek 		clength = -1;
859040ec644SDavid van Moolenbroek 		length = -1;
860040ec644SDavid van Moolenbroek 		size = -1;
861040ec644SDavid van Moolenbroek 		mtime = 0;
862040ec644SDavid van Moolenbroek 
863040ec644SDavid van Moolenbroek 		/* check port */
864040ec644SDavid van Moolenbroek 		if (!url->port)
865040ec644SDavid van Moolenbroek 			url->port = fetch_default_port(url->scheme);
866040ec644SDavid van Moolenbroek 
867040ec644SDavid van Moolenbroek 		/* were we redirected to an FTP URL? */
868040ec644SDavid van Moolenbroek 		if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) {
869040ec644SDavid van Moolenbroek 			if (strcmp(op, "GET") == 0)
870040ec644SDavid van Moolenbroek 				return (ftp_request(url, "RETR", NULL, us, purl, flags));
871040ec644SDavid van Moolenbroek 			else if (strcmp(op, "HEAD") == 0)
872040ec644SDavid van Moolenbroek 				return (ftp_request(url, "STAT", NULL, us, purl, flags));
873040ec644SDavid van Moolenbroek 		}
874040ec644SDavid van Moolenbroek 
875040ec644SDavid van Moolenbroek 		/* connect to server or proxy */
876040ec644SDavid van Moolenbroek 		if ((conn = http_connect(url, purl, flags, &cached)) == NULL)
877040ec644SDavid van Moolenbroek 			goto ouch;
878040ec644SDavid van Moolenbroek 
879040ec644SDavid van Moolenbroek 		host = url->host;
880040ec644SDavid van Moolenbroek #ifdef INET6
881040ec644SDavid van Moolenbroek 		if (strchr(url->host, ':')) {
882040ec644SDavid van Moolenbroek 			snprintf(hbuf, sizeof(hbuf), "[%s]", url->host);
883040ec644SDavid van Moolenbroek 			host = hbuf;
884040ec644SDavid van Moolenbroek 		}
885040ec644SDavid van Moolenbroek #endif
886040ec644SDavid van Moolenbroek 		if (url->port != fetch_default_port(url->scheme)) {
887040ec644SDavid van Moolenbroek 			if (host != hbuf) {
888040ec644SDavid van Moolenbroek 				strcpy(hbuf, host);
889040ec644SDavid van Moolenbroek 				host = hbuf;
890040ec644SDavid van Moolenbroek 			}
891040ec644SDavid van Moolenbroek 			snprintf(hbuf + strlen(hbuf),
892040ec644SDavid van Moolenbroek 			    sizeof(hbuf) - strlen(hbuf), ":%d", url->port);
893040ec644SDavid van Moolenbroek 		}
894040ec644SDavid van Moolenbroek 
895040ec644SDavid van Moolenbroek 		/* send request */
896040ec644SDavid van Moolenbroek 		if (verbose)
897040ec644SDavid van Moolenbroek 			fetch_info("requesting %s://%s%s",
898040ec644SDavid van Moolenbroek 			    url->scheme, host, url->doc);
899040ec644SDavid van Moolenbroek 		if (purl) {
900040ec644SDavid van Moolenbroek 			http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n",
901040ec644SDavid van Moolenbroek 			    op, url->scheme, host, url->doc);
902040ec644SDavid van Moolenbroek 		} else {
903040ec644SDavid van Moolenbroek 			http_cmd(conn, "%s %s HTTP/1.1\r\n",
904040ec644SDavid van Moolenbroek 			    op, url->doc);
905040ec644SDavid van Moolenbroek 		}
906040ec644SDavid van Moolenbroek 
907040ec644SDavid van Moolenbroek 		if (if_modified_since && url->last_modified > 0)
908040ec644SDavid van Moolenbroek 			set_if_modified_since(conn, url->last_modified);
909040ec644SDavid van Moolenbroek 
910040ec644SDavid van Moolenbroek 		/* virtual host */
911040ec644SDavid van Moolenbroek 		http_cmd(conn, "Host: %s\r\n", host);
912040ec644SDavid van Moolenbroek 
913040ec644SDavid van Moolenbroek 		/* proxy authorization */
914040ec644SDavid van Moolenbroek 		if (purl) {
915040ec644SDavid van Moolenbroek 			if (*purl->user || *purl->pwd)
916040ec644SDavid van Moolenbroek 				http_basic_auth(conn, "Proxy-Authorization",
917040ec644SDavid van Moolenbroek 				    purl->user, purl->pwd);
918040ec644SDavid van Moolenbroek 			else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
919040ec644SDavid van Moolenbroek 				http_authorize(conn, "Proxy-Authorization", p);
920040ec644SDavid van Moolenbroek 		}
921040ec644SDavid van Moolenbroek 
922040ec644SDavid van Moolenbroek 		/* server authorization */
923040ec644SDavid van Moolenbroek 		if (need_auth || *url->user || *url->pwd) {
924040ec644SDavid van Moolenbroek 			if (*url->user || *url->pwd)
925040ec644SDavid van Moolenbroek 				http_basic_auth(conn, "Authorization", url->user, url->pwd);
926040ec644SDavid van Moolenbroek 			else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
927040ec644SDavid van Moolenbroek 				http_authorize(conn, "Authorization", p);
928040ec644SDavid van Moolenbroek 			else if (fetchAuthMethod && fetchAuthMethod(url) == 0) {
929040ec644SDavid van Moolenbroek 				http_basic_auth(conn, "Authorization", url->user, url->pwd);
930040ec644SDavid van Moolenbroek 			} else {
931040ec644SDavid van Moolenbroek 				http_seterr(HTTP_NEED_AUTH);
932040ec644SDavid van Moolenbroek 				goto ouch;
933040ec644SDavid van Moolenbroek 			}
934040ec644SDavid van Moolenbroek 		}
935040ec644SDavid van Moolenbroek 
936040ec644SDavid van Moolenbroek 		/* other headers */
937040ec644SDavid van Moolenbroek 		if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') {
938040ec644SDavid van Moolenbroek 			if (strcasecmp(p, "auto") == 0)
939040ec644SDavid van Moolenbroek 				http_cmd(conn, "Referer: %s://%s%s\r\n",
940040ec644SDavid van Moolenbroek 				    url->scheme, host, url->doc);
941040ec644SDavid van Moolenbroek 			else
942040ec644SDavid van Moolenbroek 				http_cmd(conn, "Referer: %s\r\n", p);
943040ec644SDavid van Moolenbroek 		}
944040ec644SDavid van Moolenbroek 		if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0')
945040ec644SDavid van Moolenbroek 			http_cmd(conn, "User-Agent: %s\r\n", p);
946040ec644SDavid van Moolenbroek 		else
947040ec644SDavid van Moolenbroek 			http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER);
948040ec644SDavid van Moolenbroek 		if (url->offset > 0)
949040ec644SDavid van Moolenbroek 			http_cmd(conn, "Range: bytes=%lld-\r\n", (long long)url->offset);
950040ec644SDavid van Moolenbroek 		http_cmd(conn, "\r\n");
951040ec644SDavid van Moolenbroek 
952040ec644SDavid van Moolenbroek 		/*
953040ec644SDavid van Moolenbroek 		 * Force the queued request to be dispatched.  Normally, one
954040ec644SDavid van Moolenbroek 		 * would do this with shutdown(2) but squid proxies can be
955040ec644SDavid van Moolenbroek 		 * configured to disallow such half-closed connections.  To
956040ec644SDavid van Moolenbroek 		 * be compatible with such configurations, fiddle with socket
957040ec644SDavid van Moolenbroek 		 * options to force the pending data to be written.
958040ec644SDavid van Moolenbroek 		 */
959040ec644SDavid van Moolenbroek #ifdef TCP_NOPUSH
960040ec644SDavid van Moolenbroek 		val = 0;
961040ec644SDavid van Moolenbroek 		setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,
962040ec644SDavid van Moolenbroek 			   sizeof(val));
963040ec644SDavid van Moolenbroek #endif
964040ec644SDavid van Moolenbroek 		val = 1;
965040ec644SDavid van Moolenbroek 		setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,
966040ec644SDavid van Moolenbroek 		    (socklen_t)sizeof(val));
967040ec644SDavid van Moolenbroek 
968040ec644SDavid van Moolenbroek 		/* get reply */
969040ec644SDavid van Moolenbroek 		switch (http_get_reply(conn)) {
970040ec644SDavid van Moolenbroek 		case HTTP_OK:
971040ec644SDavid van Moolenbroek 		case HTTP_PARTIAL:
972040ec644SDavid van Moolenbroek 		case HTTP_NOT_MODIFIED:
973040ec644SDavid van Moolenbroek 			/* fine */
974040ec644SDavid van Moolenbroek 			break;
975040ec644SDavid van Moolenbroek 		case HTTP_MOVED_PERM:
976040ec644SDavid van Moolenbroek 		case HTTP_MOVED_TEMP:
977040ec644SDavid van Moolenbroek 		case HTTP_SEE_OTHER:
978040ec644SDavid van Moolenbroek 			/*
979040ec644SDavid van Moolenbroek 			 * Not so fine, but we still have to read the
980040ec644SDavid van Moolenbroek 			 * headers to get the new location.
981040ec644SDavid van Moolenbroek 			 */
982040ec644SDavid van Moolenbroek 			break;
983040ec644SDavid van Moolenbroek 		case HTTP_NEED_AUTH:
984040ec644SDavid van Moolenbroek 			if (need_auth) {
985040ec644SDavid van Moolenbroek 				/*
986040ec644SDavid van Moolenbroek 				 * We already sent out authorization code,
987040ec644SDavid van Moolenbroek 				 * so there's nothing more we can do.
988040ec644SDavid van Moolenbroek 				 */
989040ec644SDavid van Moolenbroek 				http_seterr(conn->err);
990040ec644SDavid van Moolenbroek 				goto ouch;
991040ec644SDavid van Moolenbroek 			}
992040ec644SDavid van Moolenbroek 			/* try again, but send the password this time */
993040ec644SDavid van Moolenbroek 			if (verbose)
994040ec644SDavid van Moolenbroek 				fetch_info("server requires authorization");
995040ec644SDavid van Moolenbroek 			break;
996040ec644SDavid van Moolenbroek 		case HTTP_NEED_PROXY_AUTH:
997040ec644SDavid van Moolenbroek 			/*
998040ec644SDavid van Moolenbroek 			 * If we're talking to a proxy, we already sent
999040ec644SDavid van Moolenbroek 			 * our proxy authorization code, so there's
1000040ec644SDavid van Moolenbroek 			 * nothing more we can do.
1001040ec644SDavid van Moolenbroek 			 */
1002040ec644SDavid van Moolenbroek 			http_seterr(conn->err);
1003040ec644SDavid van Moolenbroek 			goto ouch;
1004040ec644SDavid van Moolenbroek 		case HTTP_BAD_RANGE:
1005040ec644SDavid van Moolenbroek 			/*
1006040ec644SDavid van Moolenbroek 			 * This can happen if we ask for 0 bytes because
1007040ec644SDavid van Moolenbroek 			 * we already have the whole file.  Consider this
1008040ec644SDavid van Moolenbroek 			 * a success for now, and check sizes later.
1009040ec644SDavid van Moolenbroek 			 */
1010040ec644SDavid van Moolenbroek 			break;
1011040ec644SDavid van Moolenbroek 		case HTTP_PROTOCOL_ERROR:
1012040ec644SDavid van Moolenbroek 			/* fall through */
1013040ec644SDavid van Moolenbroek 		case -1:
1014040ec644SDavid van Moolenbroek 			--i;
1015040ec644SDavid van Moolenbroek 			if (cached)
1016040ec644SDavid van Moolenbroek 				continue;
1017040ec644SDavid van Moolenbroek 			fetch_syserr();
1018040ec644SDavid van Moolenbroek 			goto ouch;
1019040ec644SDavid van Moolenbroek 		default:
1020040ec644SDavid van Moolenbroek 			http_seterr(conn->err);
1021040ec644SDavid van Moolenbroek 			if (!verbose)
1022040ec644SDavid van Moolenbroek 				goto ouch;
1023040ec644SDavid van Moolenbroek 			/* fall through so we can get the full error message */
1024040ec644SDavid van Moolenbroek 		}
1025040ec644SDavid van Moolenbroek 
1026040ec644SDavid van Moolenbroek 		/* get headers */
1027040ec644SDavid van Moolenbroek 		do {
1028040ec644SDavid van Moolenbroek 			switch ((h = http_next_header(conn, &p))) {
1029040ec644SDavid van Moolenbroek 			case hdr_syserror:
1030040ec644SDavid van Moolenbroek 				fetch_syserr();
1031040ec644SDavid van Moolenbroek 				goto ouch;
1032040ec644SDavid van Moolenbroek 			case hdr_error:
1033040ec644SDavid van Moolenbroek 				http_seterr(HTTP_PROTOCOL_ERROR);
1034040ec644SDavid van Moolenbroek 				goto ouch;
1035040ec644SDavid van Moolenbroek 			case hdr_connection:
1036040ec644SDavid van Moolenbroek 				/* XXX too weak? */
1037040ec644SDavid van Moolenbroek 				keep_alive = (strcasecmp(p, "keep-alive") == 0);
1038040ec644SDavid van Moolenbroek 				break;
1039040ec644SDavid van Moolenbroek 			case hdr_content_length:
1040040ec644SDavid van Moolenbroek 				http_parse_length(p, &clength);
1041040ec644SDavid van Moolenbroek 				break;
1042040ec644SDavid van Moolenbroek 			case hdr_content_range:
1043040ec644SDavid van Moolenbroek 				http_parse_range(p, &offset, &length, &size);
1044040ec644SDavid van Moolenbroek 				break;
1045040ec644SDavid van Moolenbroek 			case hdr_last_modified:
1046040ec644SDavid van Moolenbroek 				http_parse_mtime(p, &mtime);
1047040ec644SDavid van Moolenbroek 				break;
1048040ec644SDavid van Moolenbroek 			case hdr_location:
1049040ec644SDavid van Moolenbroek 				if (!HTTP_REDIRECT(conn->err))
1050040ec644SDavid van Moolenbroek 					break;
1051040ec644SDavid van Moolenbroek 				if (new)
1052040ec644SDavid van Moolenbroek 					free(new);
1053040ec644SDavid van Moolenbroek 				if (verbose)
1054040ec644SDavid van Moolenbroek 					fetch_info("%d redirect to %s", conn->err, p);
1055040ec644SDavid van Moolenbroek 				if (*p == '/')
1056040ec644SDavid van Moolenbroek 					/* absolute path */
1057040ec644SDavid van Moolenbroek 					new = fetchMakeURL(url->scheme, url->host, url->port, p,
1058040ec644SDavid van Moolenbroek 					    url->user, url->pwd);
1059040ec644SDavid van Moolenbroek 				else
1060040ec644SDavid van Moolenbroek 					new = fetchParseURL(p);
1061040ec644SDavid van Moolenbroek 				if (new == NULL) {
1062040ec644SDavid van Moolenbroek 					/* XXX should set an error code */
1063040ec644SDavid van Moolenbroek 					goto ouch;
1064040ec644SDavid van Moolenbroek 				}
1065040ec644SDavid van Moolenbroek 				if (!*new->user && !*new->pwd) {
1066040ec644SDavid van Moolenbroek 					strcpy(new->user, url->user);
1067040ec644SDavid van Moolenbroek 					strcpy(new->pwd, url->pwd);
1068040ec644SDavid van Moolenbroek 				}
1069040ec644SDavid van Moolenbroek 				new->offset = url->offset;
1070040ec644SDavid van Moolenbroek 				new->length = url->length;
1071040ec644SDavid van Moolenbroek 				break;
1072040ec644SDavid van Moolenbroek 			case hdr_transfer_encoding:
1073040ec644SDavid van Moolenbroek 				/* XXX weak test*/
1074040ec644SDavid van Moolenbroek 				chunked = (strcasecmp(p, "chunked") == 0);
1075040ec644SDavid van Moolenbroek 				break;
1076040ec644SDavid van Moolenbroek 			case hdr_www_authenticate:
1077040ec644SDavid van Moolenbroek 				if (conn->err != HTTP_NEED_AUTH)
1078040ec644SDavid van Moolenbroek 					break;
1079040ec644SDavid van Moolenbroek 				/* if we were smarter, we'd check the method and realm */
1080040ec644SDavid van Moolenbroek 				break;
1081040ec644SDavid van Moolenbroek 			case hdr_end:
1082040ec644SDavid van Moolenbroek 				/* fall through */
1083040ec644SDavid van Moolenbroek 			case hdr_unknown:
1084040ec644SDavid van Moolenbroek 				/* ignore */
1085040ec644SDavid van Moolenbroek 				break;
1086040ec644SDavid van Moolenbroek 			}
1087040ec644SDavid van Moolenbroek 		} while (h > hdr_end);
1088040ec644SDavid van Moolenbroek 
1089040ec644SDavid van Moolenbroek 		/* we need to provide authentication */
1090040ec644SDavid van Moolenbroek 		if (conn->err == HTTP_NEED_AUTH) {
1091040ec644SDavid van Moolenbroek 			e = conn->err;
1092040ec644SDavid van Moolenbroek 			need_auth = 1;
1093040ec644SDavid van Moolenbroek 			fetch_close(conn);
1094040ec644SDavid van Moolenbroek 			conn = NULL;
1095040ec644SDavid van Moolenbroek 			continue;
1096040ec644SDavid van Moolenbroek 		}
1097040ec644SDavid van Moolenbroek 
1098040ec644SDavid van Moolenbroek 		/* requested range not satisfiable */
1099040ec644SDavid van Moolenbroek 		if (conn->err == HTTP_BAD_RANGE) {
1100040ec644SDavid van Moolenbroek 			if (url->offset == size && url->length == 0) {
1101040ec644SDavid van Moolenbroek 				/* asked for 0 bytes; fake it */
1102040ec644SDavid van Moolenbroek 				offset = url->offset;
1103040ec644SDavid van Moolenbroek 				conn->err = HTTP_OK;
1104040ec644SDavid van Moolenbroek 				break;
1105040ec644SDavid van Moolenbroek 			} else {
1106040ec644SDavid van Moolenbroek 				http_seterr(conn->err);
1107040ec644SDavid van Moolenbroek 				goto ouch;
1108040ec644SDavid van Moolenbroek 			}
1109040ec644SDavid van Moolenbroek 		}
1110040ec644SDavid van Moolenbroek 
1111040ec644SDavid van Moolenbroek 		/* we have a hit or an error */
1112040ec644SDavid van Moolenbroek 		if (conn->err == HTTP_OK ||
1113040ec644SDavid van Moolenbroek 		    conn->err == HTTP_PARTIAL ||
1114040ec644SDavid van Moolenbroek 		    conn->err == HTTP_NOT_MODIFIED ||
1115040ec644SDavid van Moolenbroek 		    HTTP_ERROR(conn->err))
1116040ec644SDavid van Moolenbroek 			break;
1117040ec644SDavid van Moolenbroek 
1118040ec644SDavid van Moolenbroek 		/* all other cases: we got a redirect */
1119040ec644SDavid van Moolenbroek 		e = conn->err;
1120040ec644SDavid van Moolenbroek 		need_auth = 0;
1121040ec644SDavid van Moolenbroek 		fetch_close(conn);
1122040ec644SDavid van Moolenbroek 		conn = NULL;
1123040ec644SDavid van Moolenbroek 		if (!new)
1124040ec644SDavid van Moolenbroek 			break;
1125040ec644SDavid van Moolenbroek 		if (url != URL)
1126040ec644SDavid van Moolenbroek 			fetchFreeURL(url);
1127040ec644SDavid van Moolenbroek 		url = new;
1128040ec644SDavid van Moolenbroek 	} while (++i < n);
1129040ec644SDavid van Moolenbroek 
1130040ec644SDavid van Moolenbroek 	/* we failed, or ran out of retries */
1131040ec644SDavid van Moolenbroek 	if (conn == NULL) {
1132040ec644SDavid van Moolenbroek 		http_seterr(e);
1133040ec644SDavid van Moolenbroek 		goto ouch;
1134040ec644SDavid van Moolenbroek 	}
1135040ec644SDavid van Moolenbroek 
1136040ec644SDavid van Moolenbroek 	/* check for inconsistencies */
1137040ec644SDavid van Moolenbroek 	if (clength != -1 && length != -1 && clength != length) {
1138040ec644SDavid van Moolenbroek 		http_seterr(HTTP_PROTOCOL_ERROR);
1139040ec644SDavid van Moolenbroek 		goto ouch;
1140040ec644SDavid van Moolenbroek 	}
1141040ec644SDavid van Moolenbroek 	if (clength == -1)
1142040ec644SDavid van Moolenbroek 		clength = length;
1143040ec644SDavid van Moolenbroek 	if (clength != -1)
1144040ec644SDavid van Moolenbroek 		length = offset + clength;
1145040ec644SDavid van Moolenbroek 	if (length != -1 && size != -1 && length != size) {
1146040ec644SDavid van Moolenbroek 		http_seterr(HTTP_PROTOCOL_ERROR);
1147040ec644SDavid van Moolenbroek 		goto ouch;
1148040ec644SDavid van Moolenbroek 	}
1149040ec644SDavid van Moolenbroek 	if (size == -1)
1150040ec644SDavid van Moolenbroek 		size = length;
1151040ec644SDavid van Moolenbroek 
1152040ec644SDavid van Moolenbroek 	/* fill in stats */
1153040ec644SDavid van Moolenbroek 	if (us) {
1154040ec644SDavid van Moolenbroek 		us->size = size;
1155040ec644SDavid van Moolenbroek 		us->atime = us->mtime = mtime;
1156040ec644SDavid van Moolenbroek 	}
1157040ec644SDavid van Moolenbroek 
1158040ec644SDavid van Moolenbroek 	/* too far? */
1159040ec644SDavid van Moolenbroek 	if (URL->offset > 0 && offset > URL->offset) {
1160040ec644SDavid van Moolenbroek 		http_seterr(HTTP_PROTOCOL_ERROR);
1161040ec644SDavid van Moolenbroek 		goto ouch;
1162040ec644SDavid van Moolenbroek 	}
1163040ec644SDavid van Moolenbroek 
1164040ec644SDavid van Moolenbroek 	/* report back real offset and size */
1165040ec644SDavid van Moolenbroek 	URL->offset = offset;
1166040ec644SDavid van Moolenbroek 	URL->length = clength;
1167040ec644SDavid van Moolenbroek 
1168040ec644SDavid van Moolenbroek 	if (clength == -1 && !chunked)
1169040ec644SDavid van Moolenbroek 		keep_alive = 0;
1170040ec644SDavid van Moolenbroek 
1171040ec644SDavid van Moolenbroek 	if (conn->err == HTTP_NOT_MODIFIED) {
1172040ec644SDavid van Moolenbroek 		http_seterr(HTTP_NOT_MODIFIED);
1173040ec644SDavid van Moolenbroek 		if (keep_alive) {
1174040ec644SDavid van Moolenbroek 			fetch_cache_put(conn, fetch_close);
1175040ec644SDavid van Moolenbroek 			conn = NULL;
1176040ec644SDavid van Moolenbroek 		}
1177040ec644SDavid van Moolenbroek 		goto ouch;
1178040ec644SDavid van Moolenbroek 	}
1179040ec644SDavid van Moolenbroek 
1180040ec644SDavid van Moolenbroek 	/* wrap it up in a fetchIO */
1181040ec644SDavid van Moolenbroek 	if ((f = http_funopen(conn, chunked, keep_alive, clength)) == NULL) {
1182040ec644SDavid van Moolenbroek 		fetch_syserr();
1183040ec644SDavid van Moolenbroek 		goto ouch;
1184040ec644SDavid van Moolenbroek 	}
1185040ec644SDavid van Moolenbroek 
1186040ec644SDavid van Moolenbroek 	if (url != URL)
1187040ec644SDavid van Moolenbroek 		fetchFreeURL(url);
1188040ec644SDavid van Moolenbroek 	if (purl)
1189040ec644SDavid van Moolenbroek 		fetchFreeURL(purl);
1190040ec644SDavid van Moolenbroek 
1191040ec644SDavid van Moolenbroek 	if (HTTP_ERROR(conn->err)) {
1192040ec644SDavid van Moolenbroek 
1193040ec644SDavid van Moolenbroek 		if (keep_alive) {
1194040ec644SDavid van Moolenbroek 			char buf[512];
1195040ec644SDavid van Moolenbroek 			do {
1196040ec644SDavid van Moolenbroek 			} while (fetchIO_read(f, buf, sizeof(buf)) > 0);
1197040ec644SDavid van Moolenbroek 		}
1198040ec644SDavid van Moolenbroek 
1199040ec644SDavid van Moolenbroek 		fetchIO_close(f);
1200040ec644SDavid van Moolenbroek 		f = NULL;
1201040ec644SDavid van Moolenbroek 	}
1202040ec644SDavid van Moolenbroek 
1203040ec644SDavid van Moolenbroek 	return (f);
1204040ec644SDavid van Moolenbroek 
1205040ec644SDavid van Moolenbroek ouch:
1206040ec644SDavid van Moolenbroek 	if (url != URL)
1207040ec644SDavid van Moolenbroek 		fetchFreeURL(url);
1208040ec644SDavid van Moolenbroek 	if (purl)
1209040ec644SDavid van Moolenbroek 		fetchFreeURL(purl);
1210040ec644SDavid van Moolenbroek 	if (conn != NULL)
1211040ec644SDavid van Moolenbroek 		fetch_close(conn);
1212040ec644SDavid van Moolenbroek 	return (NULL);
1213040ec644SDavid van Moolenbroek }
1214040ec644SDavid van Moolenbroek 
1215040ec644SDavid van Moolenbroek 
1216040ec644SDavid van Moolenbroek /*****************************************************************************
1217040ec644SDavid van Moolenbroek  * Entry points
1218040ec644SDavid van Moolenbroek  */
1219040ec644SDavid van Moolenbroek 
1220040ec644SDavid van Moolenbroek /*
1221040ec644SDavid van Moolenbroek  * Retrieve and stat a file by HTTP
1222040ec644SDavid van Moolenbroek  */
1223040ec644SDavid van Moolenbroek fetchIO *
fetchXGetHTTP(struct url * URL,struct url_stat * us,const char * flags)1224040ec644SDavid van Moolenbroek fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags)
1225040ec644SDavid van Moolenbroek {
1226040ec644SDavid van Moolenbroek 	return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags));
1227040ec644SDavid van Moolenbroek }
1228040ec644SDavid van Moolenbroek 
1229040ec644SDavid van Moolenbroek /*
1230040ec644SDavid van Moolenbroek  * Retrieve a file by HTTP
1231040ec644SDavid van Moolenbroek  */
1232040ec644SDavid van Moolenbroek fetchIO *
fetchGetHTTP(struct url * URL,const char * flags)1233040ec644SDavid van Moolenbroek fetchGetHTTP(struct url *URL, const char *flags)
1234040ec644SDavid van Moolenbroek {
1235040ec644SDavid van Moolenbroek 	return (fetchXGetHTTP(URL, NULL, flags));
1236040ec644SDavid van Moolenbroek }
1237040ec644SDavid van Moolenbroek 
1238040ec644SDavid van Moolenbroek /*
1239040ec644SDavid van Moolenbroek  * Store a file by HTTP
1240040ec644SDavid van Moolenbroek  */
1241040ec644SDavid van Moolenbroek fetchIO *
1242040ec644SDavid van Moolenbroek /*ARGSUSED*/
fetchPutHTTP(struct url * URL __unused,const char * flags __unused)1243040ec644SDavid van Moolenbroek fetchPutHTTP(struct url *URL __unused, const char *flags __unused)
1244040ec644SDavid van Moolenbroek {
1245040ec644SDavid van Moolenbroek 	fprintf(stderr, "fetchPutHTTP(): not implemented\n");
1246040ec644SDavid van Moolenbroek 	return (NULL);
1247040ec644SDavid van Moolenbroek }
1248040ec644SDavid van Moolenbroek 
1249040ec644SDavid van Moolenbroek /*
1250040ec644SDavid van Moolenbroek  * Get an HTTP document's metadata
1251040ec644SDavid van Moolenbroek  */
1252040ec644SDavid van Moolenbroek int
fetchStatHTTP(struct url * URL,struct url_stat * us,const char * flags)1253040ec644SDavid van Moolenbroek fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags)
1254040ec644SDavid van Moolenbroek {
1255040ec644SDavid van Moolenbroek 	fetchIO *f;
1256040ec644SDavid van Moolenbroek 
1257040ec644SDavid van Moolenbroek 	f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags);
1258040ec644SDavid van Moolenbroek 	if (f == NULL)
1259040ec644SDavid van Moolenbroek 		return (-1);
1260040ec644SDavid van Moolenbroek 	fetchIO_close(f);
1261040ec644SDavid van Moolenbroek 	return (0);
1262040ec644SDavid van Moolenbroek }
1263040ec644SDavid van Moolenbroek 
1264040ec644SDavid van Moolenbroek enum http_states {
1265040ec644SDavid van Moolenbroek 	ST_NONE,
1266040ec644SDavid van Moolenbroek 	ST_LT,
1267040ec644SDavid van Moolenbroek 	ST_LTA,
1268040ec644SDavid van Moolenbroek 	ST_TAGA,
1269040ec644SDavid van Moolenbroek 	ST_H,
1270040ec644SDavid van Moolenbroek 	ST_R,
1271040ec644SDavid van Moolenbroek 	ST_E,
1272040ec644SDavid van Moolenbroek 	ST_F,
1273040ec644SDavid van Moolenbroek 	ST_HREF,
1274040ec644SDavid van Moolenbroek 	ST_HREFQ,
1275040ec644SDavid van Moolenbroek 	ST_TAG,
1276040ec644SDavid van Moolenbroek 	ST_TAGAX,
1277040ec644SDavid van Moolenbroek 	ST_TAGAQ
1278040ec644SDavid van Moolenbroek };
1279040ec644SDavid van Moolenbroek 
1280040ec644SDavid van Moolenbroek struct index_parser {
1281040ec644SDavid van Moolenbroek 	struct url_list *ue;
1282040ec644SDavid van Moolenbroek 	struct url *url;
1283040ec644SDavid van Moolenbroek 	enum http_states state;
1284040ec644SDavid van Moolenbroek };
1285040ec644SDavid van Moolenbroek 
1286040ec644SDavid van Moolenbroek static ssize_t
parse_index(struct index_parser * parser,const char * buf,size_t len)1287040ec644SDavid van Moolenbroek parse_index(struct index_parser *parser, const char *buf, size_t len)
1288040ec644SDavid van Moolenbroek {
1289040ec644SDavid van Moolenbroek 	char *end_attr, p = *buf;
1290040ec644SDavid van Moolenbroek 
1291040ec644SDavid van Moolenbroek 	switch (parser->state) {
1292040ec644SDavid van Moolenbroek 	case ST_NONE:
1293040ec644SDavid van Moolenbroek 		/* Plain text, not in markup */
1294040ec644SDavid van Moolenbroek 		if (p == '<')
1295040ec644SDavid van Moolenbroek 			parser->state = ST_LT;
1296040ec644SDavid van Moolenbroek 		return 1;
1297040ec644SDavid van Moolenbroek 	case ST_LT:
1298040ec644SDavid van Moolenbroek 		/* In tag -- "<" already found */
1299040ec644SDavid van Moolenbroek 		if (p == '>')
1300040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1301040ec644SDavid van Moolenbroek 		else if (p == 'a' || p == 'A')
1302040ec644SDavid van Moolenbroek 			parser->state = ST_LTA;
1303040ec644SDavid van Moolenbroek 		else if (!isspace((unsigned char)p))
1304040ec644SDavid van Moolenbroek 			parser->state = ST_TAG;
1305040ec644SDavid van Moolenbroek 		return 1;
1306040ec644SDavid van Moolenbroek 	case ST_LTA:
1307040ec644SDavid van Moolenbroek 		/* In tag -- "<a" already found */
1308040ec644SDavid van Moolenbroek 		if (p == '>')
1309040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1310040ec644SDavid van Moolenbroek 		else if (p == '"')
1311040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1312040ec644SDavid van Moolenbroek 		else if (isspace((unsigned char)p))
1313040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1314040ec644SDavid van Moolenbroek 		else
1315040ec644SDavid van Moolenbroek 			parser->state = ST_TAG;
1316040ec644SDavid van Moolenbroek 		return 1;
1317040ec644SDavid van Moolenbroek 	case ST_TAG:
1318040ec644SDavid van Moolenbroek 		/* In tag, but not "<a" -- disregard */
1319040ec644SDavid van Moolenbroek 		if (p == '>')
1320040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1321040ec644SDavid van Moolenbroek 		return 1;
1322040ec644SDavid van Moolenbroek 	case ST_TAGA:
1323040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a " already found */
1324040ec644SDavid van Moolenbroek 		if (p == '>')
1325040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1326040ec644SDavid van Moolenbroek 		else if (p == '"')
1327040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1328040ec644SDavid van Moolenbroek 		else if (p == 'h' || p == 'H')
1329040ec644SDavid van Moolenbroek 			parser->state = ST_H;
1330040ec644SDavid van Moolenbroek 		else if (!isspace((unsigned char)p))
1331040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAX;
1332040ec644SDavid van Moolenbroek 		return 1;
1333040ec644SDavid van Moolenbroek 	case ST_TAGAX:
1334040ec644SDavid van Moolenbroek 		/* In unknown keyword in a-tag */
1335040ec644SDavid van Moolenbroek 		if (p == '>')
1336040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1337040ec644SDavid van Moolenbroek 		else if (p == '"')
1338040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1339040ec644SDavid van Moolenbroek 		else if (isspace((unsigned char)p))
1340040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1341040ec644SDavid van Moolenbroek 		return 1;
1342040ec644SDavid van Moolenbroek 	case ST_TAGAQ:
1343040ec644SDavid van Moolenbroek 		/* In a-tag, unknown argument for keys. */
1344040ec644SDavid van Moolenbroek 		if (p == '>')
1345040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1346040ec644SDavid van Moolenbroek 		else if (p == '"')
1347040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1348040ec644SDavid van Moolenbroek 		return 1;
1349040ec644SDavid van Moolenbroek 	case ST_H:
1350040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a h" already found */
1351040ec644SDavid van Moolenbroek 		if (p == '>')
1352040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1353040ec644SDavid van Moolenbroek 		else if (p == '"')
1354040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1355040ec644SDavid van Moolenbroek 		else if (p == 'r' || p == 'R')
1356040ec644SDavid van Moolenbroek 			parser->state = ST_R;
1357040ec644SDavid van Moolenbroek 		else if (isspace((unsigned char)p))
1358040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1359040ec644SDavid van Moolenbroek 		else
1360040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAX;
1361040ec644SDavid van Moolenbroek 		return 1;
1362040ec644SDavid van Moolenbroek 	case ST_R:
1363040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a hr" already found */
1364040ec644SDavid van Moolenbroek 		if (p == '>')
1365040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1366040ec644SDavid van Moolenbroek 		else if (p == '"')
1367040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1368040ec644SDavid van Moolenbroek 		else if (p == 'e' || p == 'E')
1369040ec644SDavid van Moolenbroek 			parser->state = ST_E;
1370040ec644SDavid van Moolenbroek 		else if (isspace((unsigned char)p))
1371040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1372040ec644SDavid van Moolenbroek 		else
1373040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAX;
1374040ec644SDavid van Moolenbroek 		return 1;
1375040ec644SDavid van Moolenbroek 	case ST_E:
1376040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a hre" already found */
1377040ec644SDavid van Moolenbroek 		if (p == '>')
1378040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1379040ec644SDavid van Moolenbroek 		else if (p == '"')
1380040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1381040ec644SDavid van Moolenbroek 		else if (p == 'f' || p == 'F')
1382040ec644SDavid van Moolenbroek 			parser->state = ST_F;
1383040ec644SDavid van Moolenbroek 		else if (isspace((unsigned char)p))
1384040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1385040ec644SDavid van Moolenbroek 		else
1386040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAX;
1387040ec644SDavid van Moolenbroek 		return 1;
1388040ec644SDavid van Moolenbroek 	case ST_F:
1389040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a href" already found */
1390040ec644SDavid van Moolenbroek 		if (p == '>')
1391040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1392040ec644SDavid van Moolenbroek 		else if (p == '"')
1393040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAQ;
1394040ec644SDavid van Moolenbroek 		else if (p == '=')
1395040ec644SDavid van Moolenbroek 			parser->state = ST_HREF;
1396040ec644SDavid van Moolenbroek 		else if (!isspace((unsigned char)p))
1397040ec644SDavid van Moolenbroek 			parser->state = ST_TAGAX;
1398040ec644SDavid van Moolenbroek 		return 1;
1399040ec644SDavid van Moolenbroek 	case ST_HREF:
1400040ec644SDavid van Moolenbroek 		/* In a-tag -- "<a href=" already found */
1401040ec644SDavid van Moolenbroek 		if (p == '>')
1402040ec644SDavid van Moolenbroek 			parser->state = ST_NONE;
1403040ec644SDavid van Moolenbroek 		else if (p == '"')
1404040ec644SDavid van Moolenbroek 			parser->state = ST_HREFQ;
1405040ec644SDavid van Moolenbroek 		else if (!isspace((unsigned char)p))
1406040ec644SDavid van Moolenbroek 			parser->state = ST_TAGA;
1407040ec644SDavid van Moolenbroek 		return 1;
1408040ec644SDavid van Moolenbroek 	case ST_HREFQ:
1409040ec644SDavid van Moolenbroek 		/* In href of the a-tag */
1410040ec644SDavid van Moolenbroek 		end_attr = memchr(buf, '"', len);
1411040ec644SDavid van Moolenbroek 		if (end_attr == NULL)
1412040ec644SDavid van Moolenbroek 			return 0;
1413040ec644SDavid van Moolenbroek 		*end_attr = '\0';
1414040ec644SDavid van Moolenbroek 		parser->state = ST_TAGA;
1415040ec644SDavid van Moolenbroek 		if (fetch_add_entry(parser->ue, parser->url, buf, 1))
1416040ec644SDavid van Moolenbroek 			return -1;
1417040ec644SDavid van Moolenbroek 		return end_attr + 1 - buf;
1418040ec644SDavid van Moolenbroek 	}
1419040ec644SDavid van Moolenbroek 	/* NOTREACHED */
1420040ec644SDavid van Moolenbroek 	abort();
1421040ec644SDavid van Moolenbroek }
1422040ec644SDavid van Moolenbroek 
1423040ec644SDavid van Moolenbroek struct http_index_cache {
1424040ec644SDavid van Moolenbroek 	struct http_index_cache *next;
1425040ec644SDavid van Moolenbroek 	struct url *location;
1426040ec644SDavid van Moolenbroek 	struct url_list ue;
1427040ec644SDavid van Moolenbroek };
1428040ec644SDavid van Moolenbroek 
1429040ec644SDavid van Moolenbroek static struct http_index_cache *index_cache;
1430040ec644SDavid van Moolenbroek 
1431040ec644SDavid van Moolenbroek /*
1432040ec644SDavid van Moolenbroek  * List a directory
1433040ec644SDavid van Moolenbroek  */
1434040ec644SDavid van Moolenbroek int
1435040ec644SDavid van Moolenbroek /*ARGSUSED*/
fetchListHTTP(struct url_list * ue,struct url * url,const char * pattern __unused,const char * flags)1436040ec644SDavid van Moolenbroek fetchListHTTP(struct url_list *ue, struct url *url, const char *pattern __unused, const char *flags)
1437040ec644SDavid van Moolenbroek {
1438040ec644SDavid van Moolenbroek 	fetchIO *f;
1439040ec644SDavid van Moolenbroek 	char buf[2 * PATH_MAX];
1440040ec644SDavid van Moolenbroek 	size_t buf_len, sum_processed;
1441040ec644SDavid van Moolenbroek 	ssize_t read_len, processed;
1442040ec644SDavid van Moolenbroek 	struct index_parser state;
1443040ec644SDavid van Moolenbroek 	struct http_index_cache *cache = NULL;
1444040ec644SDavid van Moolenbroek 	int do_cache, ret;
1445040ec644SDavid van Moolenbroek 
1446040ec644SDavid van Moolenbroek 	do_cache = CHECK_FLAG('c');
1447040ec644SDavid van Moolenbroek 
1448040ec644SDavid van Moolenbroek 	if (do_cache) {
1449040ec644SDavid van Moolenbroek 		for (cache = index_cache; cache != NULL; cache = cache->next) {
1450040ec644SDavid van Moolenbroek 			if (strcmp(cache->location->scheme, url->scheme))
1451040ec644SDavid van Moolenbroek 				continue;
1452040ec644SDavid van Moolenbroek 			if (strcmp(cache->location->user, url->user))
1453040ec644SDavid van Moolenbroek 				continue;
1454040ec644SDavid van Moolenbroek 			if (strcmp(cache->location->pwd, url->pwd))
1455040ec644SDavid van Moolenbroek 				continue;
1456040ec644SDavid van Moolenbroek 			if (strcmp(cache->location->host, url->host))
1457040ec644SDavid van Moolenbroek 				continue;
1458040ec644SDavid van Moolenbroek 			if (cache->location->port != url->port)
1459040ec644SDavid van Moolenbroek 				continue;
1460040ec644SDavid van Moolenbroek 			if (strcmp(cache->location->doc, url->doc))
1461040ec644SDavid van Moolenbroek 				continue;
1462040ec644SDavid van Moolenbroek 			return fetchAppendURLList(ue, &cache->ue);
1463040ec644SDavid van Moolenbroek 		}
1464040ec644SDavid van Moolenbroek 
1465040ec644SDavid van Moolenbroek 		cache = malloc(sizeof(*cache));
1466040ec644SDavid van Moolenbroek 		fetchInitURLList(&cache->ue);
1467040ec644SDavid van Moolenbroek 		cache->location = fetchCopyURL(url);
1468040ec644SDavid van Moolenbroek 	}
1469040ec644SDavid van Moolenbroek 
1470040ec644SDavid van Moolenbroek 	f = fetchGetHTTP(url, flags);
1471040ec644SDavid van Moolenbroek 	if (f == NULL) {
1472040ec644SDavid van Moolenbroek 		if (do_cache) {
1473040ec644SDavid van Moolenbroek 			fetchFreeURLList(&cache->ue);
1474040ec644SDavid van Moolenbroek 			fetchFreeURL(cache->location);
1475040ec644SDavid van Moolenbroek 			free(cache);
1476040ec644SDavid van Moolenbroek 		}
1477040ec644SDavid van Moolenbroek 		return -1;
1478040ec644SDavid van Moolenbroek 	}
1479040ec644SDavid van Moolenbroek 
1480040ec644SDavid van Moolenbroek 	state.url = url;
1481040ec644SDavid van Moolenbroek 	state.state = ST_NONE;
1482040ec644SDavid van Moolenbroek 	if (do_cache) {
1483040ec644SDavid van Moolenbroek 		state.ue = &cache->ue;
1484040ec644SDavid van Moolenbroek 	} else {
1485040ec644SDavid van Moolenbroek 		state.ue = ue;
1486040ec644SDavid van Moolenbroek 	}
1487040ec644SDavid van Moolenbroek 
1488040ec644SDavid van Moolenbroek 	buf_len = 0;
1489040ec644SDavid van Moolenbroek 
1490040ec644SDavid van Moolenbroek 	while ((read_len = fetchIO_read(f, buf + buf_len, sizeof(buf) - buf_len)) > 0) {
1491040ec644SDavid van Moolenbroek 		buf_len += read_len;
1492040ec644SDavid van Moolenbroek 		sum_processed = 0;
1493040ec644SDavid van Moolenbroek 		do {
1494040ec644SDavid van Moolenbroek 			processed = parse_index(&state, buf + sum_processed, buf_len);
1495040ec644SDavid van Moolenbroek 			if (processed == -1)
1496040ec644SDavid van Moolenbroek 				break;
1497040ec644SDavid van Moolenbroek 			buf_len -= processed;
1498040ec644SDavid van Moolenbroek 			sum_processed += processed;
1499040ec644SDavid van Moolenbroek 		} while (processed != 0 && buf_len > 0);
1500040ec644SDavid van Moolenbroek 		if (processed == -1) {
1501040ec644SDavid van Moolenbroek 			read_len = -1;
1502040ec644SDavid van Moolenbroek 			break;
1503040ec644SDavid van Moolenbroek 		}
1504040ec644SDavid van Moolenbroek 		memmove(buf, buf + sum_processed, buf_len);
1505040ec644SDavid van Moolenbroek 	}
1506040ec644SDavid van Moolenbroek 
1507040ec644SDavid van Moolenbroek 	fetchIO_close(f);
1508040ec644SDavid van Moolenbroek 
1509040ec644SDavid van Moolenbroek 	ret = read_len < 0 ? -1 : 0;
1510040ec644SDavid van Moolenbroek 
1511040ec644SDavid van Moolenbroek 	if (do_cache) {
1512040ec644SDavid van Moolenbroek 		if (ret == 0) {
1513040ec644SDavid van Moolenbroek 			cache->next = index_cache;
1514040ec644SDavid van Moolenbroek 			index_cache = cache;
1515040ec644SDavid van Moolenbroek 		}
1516040ec644SDavid van Moolenbroek 
1517040ec644SDavid van Moolenbroek 		if (fetchAppendURLList(ue, &cache->ue))
1518040ec644SDavid van Moolenbroek 			ret = -1;
1519040ec644SDavid van Moolenbroek 	}
1520040ec644SDavid van Moolenbroek 
1521040ec644SDavid van Moolenbroek 	return ret;
1522040ec644SDavid van Moolenbroek }
1523