Tue Jan 7 02:13:00 2014 UTC ()
Annotate format strings. Add a bunch of int casts for size limits.


(joerg)
diff -r1.1.1.7 -r1.2 src/external/bsd/fetch/dist/libfetch/common.h
diff -r1.6 -r1.7 src/external/bsd/fetch/dist/libfetch/ftp.c
diff -r1.2 -r1.3 src/external/bsd/fetch/dist/libfetch/http.c

cvs diff -r1.1.1.7 -r1.2 src/external/bsd/fetch/dist/libfetch/common.h (switch to unified diff)

--- src/external/bsd/fetch/dist/libfetch/common.h 2010/03/24 20:51:42 1.1.1.7
+++ src/external/bsd/fetch/dist/libfetch/common.h 2014/01/07 02:13:00 1.2
@@ -1,139 +1,139 @@ @@ -1,139 +1,139 @@
1/* $NetBSD: common.h,v 1.1.1.7 2010/03/24 20:51:42 joerg Exp $ */ 1/* $NetBSD: common.h,v 1.2 2014/01/07 02:13:00 joerg Exp $ */
2/*- 2/*-
3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav 3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4 * All rights reserved. 4 * All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer 10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged. 11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products 15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission 16 * derived from this software without specific prior written permission
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * 28 *
29 * $FreeBSD: common.h,v 1.30 2007/12/18 11:03:07 des Exp $ 29 * $FreeBSD: common.h,v 1.30 2007/12/18 11:03:07 des Exp $
30 */ 30 */
31 31
32#ifndef _COMMON_H_INCLUDED 32#ifndef _COMMON_H_INCLUDED
33#define _COMMON_H_INCLUDED 33#define _COMMON_H_INCLUDED
34 34
35#define FTP_DEFAULT_PORT 21 35#define FTP_DEFAULT_PORT 21
36#define HTTP_DEFAULT_PORT 80 36#define HTTP_DEFAULT_PORT 80
37#define FTP_DEFAULT_PROXY_PORT 21 37#define FTP_DEFAULT_PROXY_PORT 21
38#define HTTP_DEFAULT_PROXY_PORT 3128 38#define HTTP_DEFAULT_PROXY_PORT 3128
39 39
40#ifdef WITH_SSL 40#ifdef WITH_SSL
41#include <openssl/crypto.h> 41#include <openssl/crypto.h>
42#include <openssl/x509.h> 42#include <openssl/x509.h>
43#include <openssl/pem.h> 43#include <openssl/pem.h>
44#include <openssl/ssl.h> 44#include <openssl/ssl.h>
45#include <openssl/err.h> 45#include <openssl/err.h>
46#endif 46#endif
47 47
48#if !defined(__sun) && !defined(__hpux) && !defined(__INTERIX) && \ 48#if !defined(__sun) && !defined(__hpux) && !defined(__INTERIX) && \
49 !defined(__digital__) && !defined(__linux) && !defined(__MINT__) && \ 49 !defined(__digital__) && !defined(__linux) && !defined(__MINT__) && \
50 !defined(__sgi) 50 !defined(__sgi)
51#define HAVE_SA_LEN 51#define HAVE_SA_LEN
52#endif 52#endif
53 53
54/* Connection */ 54/* Connection */
55typedef struct fetchconn conn_t; 55typedef struct fetchconn conn_t;
56 56
57struct fetchconn { 57struct fetchconn {
58 int sd; /* socket descriptor */ 58 int sd; /* socket descriptor */
59 char *buf; /* buffer */ 59 char *buf; /* buffer */
60 size_t bufsize; /* buffer size */ 60 size_t bufsize; /* buffer size */
61 size_t buflen; /* length of buffer contents */ 61 size_t buflen; /* length of buffer contents */
62 char *next_buf; /* pending buffer, e.g. after getln */ 62 char *next_buf; /* pending buffer, e.g. after getln */
63 size_t next_len; /* size of pending buffer */ 63 size_t next_len; /* size of pending buffer */
64 int err; /* last protocol reply code */ 64 int err; /* last protocol reply code */
65#ifdef WITH_SSL 65#ifdef WITH_SSL
66 SSL *ssl; /* SSL handle */ 66 SSL *ssl; /* SSL handle */
67 SSL_CTX *ssl_ctx; /* SSL context */ 67 SSL_CTX *ssl_ctx; /* SSL context */
68 X509 *ssl_cert; /* server certificate */ 68 X509 *ssl_cert; /* server certificate */
69# if OPENSSL_VERSION_NUMBER < 0x00909000L 69# if OPENSSL_VERSION_NUMBER < 0x00909000L
70 SSL_METHOD *ssl_meth; /* SSL method */ 70 SSL_METHOD *ssl_meth; /* SSL method */
71# else 71# else
72 const SSL_METHOD *ssl_meth; /* SSL method */ 72 const SSL_METHOD *ssl_meth; /* SSL method */
73# endif 73# endif
74#endif 74#endif
75 75
76 char *ftp_home; 76 char *ftp_home;
77 77
78 struct url *cache_url; 78 struct url *cache_url;
79 int cache_af; 79 int cache_af;
80 int (*cache_close)(conn_t *); 80 int (*cache_close)(conn_t *);
81 conn_t *next_cached; 81 conn_t *next_cached;
82}; 82};
83 83
84/* Structure used for error message lists */ 84/* Structure used for error message lists */
85struct fetcherr { 85struct fetcherr {
86 const int num; 86 const int num;
87 const int cat; 87 const int cat;
88 const char *string; 88 const char *string;
89}; 89};
90 90
91void fetch_seterr(struct fetcherr *, int); 91void fetch_seterr(struct fetcherr *, int);
92void fetch_syserr(void); 92void fetch_syserr(void);
93void fetch_info(const char *, ...); 93void fetch_info(const char *, ...) __printflike(1, 2);
94int fetch_default_port(const char *); 94int fetch_default_port(const char *);
95int fetch_default_proxy_port(const char *); 95int fetch_default_proxy_port(const char *);
96int fetch_bind(int, int, const char *); 96int fetch_bind(int, int, const char *);
97conn_t *fetch_cache_get(const struct url *, int); 97conn_t *fetch_cache_get(const struct url *, int);
98void fetch_cache_put(conn_t *, int (*)(conn_t *)); 98void fetch_cache_put(conn_t *, int (*)(conn_t *));
99conn_t *fetch_connect(struct url *, int, int); 99conn_t *fetch_connect(struct url *, int, int);
100conn_t *fetch_reopen(int); 100conn_t *fetch_reopen(int);
101int fetch_ssl(conn_t *, int); 101int fetch_ssl(conn_t *, int);
102ssize_t fetch_read(conn_t *, char *, size_t); 102ssize_t fetch_read(conn_t *, char *, size_t);
103int fetch_getln(conn_t *); 103int fetch_getln(conn_t *);
104ssize_t fetch_write(conn_t *, const void *, size_t); 104ssize_t fetch_write(conn_t *, const void *, size_t);
105int fetch_close(conn_t *); 105int fetch_close(conn_t *);
106int fetch_add_entry(struct url_list *, struct url *, const char *, int); 106int fetch_add_entry(struct url_list *, struct url *, const char *, int);
107int fetch_netrc_auth(struct url *url); 107int fetch_netrc_auth(struct url *url);
108int fetch_no_proxy_match(const char *); 108int fetch_no_proxy_match(const char *);
109int fetch_urlpath_safe(char); 109int fetch_urlpath_safe(char);
110 110
111#define ftp_seterr(n) fetch_seterr(ftp_errlist, n) 111#define ftp_seterr(n) fetch_seterr(ftp_errlist, n)
112#define http_seterr(n) fetch_seterr(http_errlist, n) 112#define http_seterr(n) fetch_seterr(http_errlist, n)
113#define netdb_seterr(n) fetch_seterr(netdb_errlist, n) 113#define netdb_seterr(n) fetch_seterr(netdb_errlist, n)
114#define url_seterr(n) fetch_seterr(url_errlist, n) 114#define url_seterr(n) fetch_seterr(url_errlist, n)
115 115
116fetchIO *fetchIO_unopen(void *, ssize_t (*)(void *, void *, size_t), 116fetchIO *fetchIO_unopen(void *, ssize_t (*)(void *, void *, size_t),
117 ssize_t (*)(void *, const void *, size_t), void (*)(void *)); 117 ssize_t (*)(void *, const void *, size_t), void (*)(void *));
118 118
119/* 119/*
120 * I don't really like exporting http_request() and ftp_request(), 120 * I don't really like exporting http_request() and ftp_request(),
121 * but the HTTP and FTP code occasionally needs to cross-call 121 * but the HTTP and FTP code occasionally needs to cross-call
122 * eachother, and this saves me from adding a lot of special-case code 122 * eachother, and this saves me from adding a lot of special-case code
123 * to handle those cases. 123 * to handle those cases.
124 * 124 *
125 * Note that _*_request() free purl, which is way ugly but saves us a 125 * Note that _*_request() free purl, which is way ugly but saves us a
126 * whole lot of trouble. 126 * whole lot of trouble.
127 */ 127 */
128fetchIO *http_request(struct url *, const char *, 128fetchIO *http_request(struct url *, const char *,
129 struct url_stat *, struct url *, const char *); 129 struct url_stat *, struct url *, const char *);
130fetchIO *ftp_request(struct url *, const char *, const char *, 130fetchIO *ftp_request(struct url *, const char *, const char *,
131 struct url_stat *, struct url *, const char *); 131 struct url_stat *, struct url *, const char *);
132 132
133 133
134/* 134/*
135 * Check whether a particular flag is set 135 * Check whether a particular flag is set
136 */ 136 */
137#define CHECK_FLAG(x) (flags && strchr(flags, (x))) 137#define CHECK_FLAG(x) (flags && strchr(flags, (x)))
138 138
139#endif 139#endif

cvs diff -r1.6 -r1.7 src/external/bsd/fetch/dist/libfetch/ftp.c (switch to unified diff)

--- src/external/bsd/fetch/dist/libfetch/ftp.c 2013/10/19 22:58:40 1.6
+++ src/external/bsd/fetch/dist/libfetch/ftp.c 2014/01/07 02:13:00 1.7
@@ -1,1311 +1,1312 @@ @@ -1,1311 +1,1312 @@
1/* $NetBSD: ftp.c,v 1.6 2013/10/19 22:58:40 mrg Exp $ */ 1/* $NetBSD: ftp.c,v 1.7 2014/01/07 02:13:00 joerg Exp $ */
2/*- 2/*-
3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav 3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4 * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 4 * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer 11 * notice, this list of conditions and the following disclaimer
12 * in this position and unchanged. 12 * in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright 13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the 14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution. 15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products 16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission 17 * derived from this software without specific prior written permission
18 * 18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * 29 *
30 * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ 30 * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $
31 */ 31 */
32 32
33/* 33/*
34 * Portions of this code were taken from or based on ftpio.c: 34 * Portions of this code were taken from or based on ftpio.c:
35 * 35 *
36 * ---------------------------------------------------------------------------- 36 * ----------------------------------------------------------------------------
37 * "THE BEER-WARE LICENSE" (Revision 42): 37 * "THE BEER-WARE LICENSE" (Revision 42):
38 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 38 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
39 * can do whatever you want with this stuff. If we meet some day, and you think 39 * can do whatever you want with this stuff. If we meet some day, and you think
40 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 40 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
41 * ---------------------------------------------------------------------------- 41 * ----------------------------------------------------------------------------
42 * 42 *
43 * Major Changelog: 43 * Major Changelog:
44 * 44 *
45 * Dag-Erling Coïdan Smørgrav 45 * Dag-Erling Coïdan Smørgrav
46 * 9 Jun 1998 46 * 9 Jun 1998
47 * 47 *
48 * Incorporated into libfetch 48 * Incorporated into libfetch
49 * 49 *
50 * Jordan K. Hubbard 50 * Jordan K. Hubbard
51 * 17 Jan 1996 51 * 17 Jan 1996
52 * 52 *
53 * Turned inside out. Now returns xfers as new file ids, not as a special 53 * Turned inside out. Now returns xfers as new file ids, not as a special
54 * `state' of FTP_t 54 * `state' of FTP_t
55 * 55 *
56 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 56 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
57 * 57 *
58 */ 58 */
59 59
60#ifdef __linux__ 60#ifdef __linux__
61/* Keep this down to Linux, it can create surprises else where. */ 61/* Keep this down to Linux, it can create surprises else where. */
62#define _GNU_SOURCE 62#define _GNU_SOURCE
63#endif 63#endif
64 64
65#if HAVE_CONFIG_H 65#if HAVE_CONFIG_H
66#include "config.h" 66#include "config.h"
67#endif 67#endif
68#ifndef NETBSD 68#ifndef NETBSD
69#include <nbcompat.h> 69#include <nbcompat.h>
70#endif 70#endif
71 71
72#include <sys/types.h> 72#include <sys/types.h>
73#include <sys/socket.h> 73#include <sys/socket.h>
74 74
75#include <netinet/in.h> 75#include <netinet/in.h>
76#include <arpa/inet.h> 76#include <arpa/inet.h>
77 77
78#include <ctype.h> 78#include <ctype.h>
79#include <errno.h> 79#include <errno.h>
80#include <fcntl.h> 80#include <fcntl.h>
81#if defined(HAVE_INTTYPES_H) || defined(NETBSD) 81#if defined(HAVE_INTTYPES_H) || defined(NETBSD)
82#include <inttypes.h> 82#include <inttypes.h>
83#endif 83#endif
84#include <stdarg.h> 84#include <stdarg.h>
85#ifndef NETBSD 85#ifndef NETBSD
86#include <nbcompat/netdb.h> 86#include <nbcompat/netdb.h>
87#include <nbcompat/stdio.h> 87#include <nbcompat/stdio.h>
88#else 88#else
89#include <netdb.h> 89#include <netdb.h>
90#include <stdio.h> 90#include <stdio.h>
91#endif 91#endif
92#include <stdlib.h> 92#include <stdlib.h>
93#include <string.h> 93#include <string.h>
94#include <time.h> 94#include <time.h>
95#include <unistd.h> 95#include <unistd.h>
96 96
97#include "fetch.h" 97#include "fetch.h"
98#include "common.h" 98#include "common.h"
99#include "ftperr.h" 99#include "ftperr.h"
100 100
101#define FTP_ANONYMOUS_USER "anonymous" 101#define FTP_ANONYMOUS_USER "anonymous"
102 102
103#define FTP_CONNECTION_ALREADY_OPEN 125 103#define FTP_CONNECTION_ALREADY_OPEN 125
104#define FTP_OPEN_DATA_CONNECTION 150 104#define FTP_OPEN_DATA_CONNECTION 150
105#define FTP_OK 200 105#define FTP_OK 200
106#define FTP_FILE_STATUS 213 106#define FTP_FILE_STATUS 213
107#define FTP_SERVICE_READY 220 107#define FTP_SERVICE_READY 220
108#define FTP_TRANSFER_COMPLETE 226 108#define FTP_TRANSFER_COMPLETE 226
109#define FTP_PASSIVE_MODE 227 109#define FTP_PASSIVE_MODE 227
110#define FTP_LPASSIVE_MODE 228 110#define FTP_LPASSIVE_MODE 228
111#define FTP_EPASSIVE_MODE 229 111#define FTP_EPASSIVE_MODE 229
112#define FTP_LOGGED_IN 230 112#define FTP_LOGGED_IN 230
113#define FTP_FILE_ACTION_OK 250 113#define FTP_FILE_ACTION_OK 250
114#define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ 114#define FTP_DIRECTORY_CREATED 257 /* multiple meanings */
115#define FTP_FILE_CREATED 257 /* multiple meanings */ 115#define FTP_FILE_CREATED 257 /* multiple meanings */
116#define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ 116#define FTP_WORKING_DIRECTORY 257 /* multiple meanings */
117#define FTP_NEED_PASSWORD 331 117#define FTP_NEED_PASSWORD 331
118#define FTP_NEED_ACCOUNT 332 118#define FTP_NEED_ACCOUNT 332
119#define FTP_FILE_OK 350 119#define FTP_FILE_OK 350
120#define FTP_SYNTAX_ERROR 500 120#define FTP_SYNTAX_ERROR 500
121#define FTP_PROTOCOL_ERROR 999 121#define FTP_PROTOCOL_ERROR 999
122 122
123#define isftpreply(foo) \ 123#define isftpreply(foo) \
124 (isdigit((unsigned char)foo[0]) && \ 124 (isdigit((unsigned char)foo[0]) && \
125 isdigit((unsigned char)foo[1]) && \ 125 isdigit((unsigned char)foo[1]) && \
126 isdigit((unsigned char)foo[2]) && \ 126 isdigit((unsigned char)foo[2]) && \
127 (foo[3] == ' ' || foo[3] == '\0')) 127 (foo[3] == ' ' || foo[3] == '\0'))
128#define isftpinfo(foo) \ 128#define isftpinfo(foo) \
129 (isdigit((unsigned char)foo[0]) && \ 129 (isdigit((unsigned char)foo[0]) && \
130 isdigit((unsigned char)foo[1]) && \ 130 isdigit((unsigned char)foo[1]) && \
131 isdigit((unsigned char)foo[2]) && \ 131 isdigit((unsigned char)foo[2]) && \
132 foo[3] == '-') 132 foo[3] == '-')
133 133
134/* 134/*
135 * Translate IPv4 mapped IPv6 address to IPv4 address 135 * Translate IPv4 mapped IPv6 address to IPv4 address
136 */ 136 */
137static void 137static void
138unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) 138unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len)
139{ 139{
140 struct sockaddr_in *sin4; 140 struct sockaddr_in *sin4;
141 void *addrp; 141 void *addrp;
142 uint32_t addr; 142 uint32_t addr;
143 int port; 143 int port;
144 144
145 if (sin6->sin6_family != AF_INET6 || 145 if (sin6->sin6_family != AF_INET6 ||
146 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 146 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
147 return; 147 return;
148 sin4 = (struct sockaddr_in *)(void *)sin6; 148 sin4 = (struct sockaddr_in *)(void *)sin6;
149 addrp = &sin6->sin6_addr.s6_addr[12]; 149 addrp = &sin6->sin6_addr.s6_addr[12];
150 addr = *(uint32_t *)addrp; 150 addr = *(uint32_t *)addrp;
151 port = sin6->sin6_port; 151 port = sin6->sin6_port;
152 memset(sin4, 0, sizeof(struct sockaddr_in)); 152 memset(sin4, 0, sizeof(struct sockaddr_in));
153 sin4->sin_addr.s_addr = addr; 153 sin4->sin_addr.s_addr = addr;
154 sin4->sin_port = port; 154 sin4->sin_port = port;
155 sin4->sin_family = AF_INET; 155 sin4->sin_family = AF_INET;
156 *len = sizeof(struct sockaddr_in); 156 *len = sizeof(struct sockaddr_in);
157#ifdef HAVE_SA_LEN 157#ifdef HAVE_SA_LEN
158 sin4->sin_len = sizeof(struct sockaddr_in); 158 sin4->sin_len = sizeof(struct sockaddr_in);
159#endif 159#endif
160} 160}
161 161
162/* 162/*
163 * Get server response 163 * Get server response
164 */ 164 */
165static int 165static int
166ftp_chkerr(conn_t *conn) 166ftp_chkerr(conn_t *conn)
167{ 167{
168 if (fetch_getln(conn) == -1) { 168 if (fetch_getln(conn) == -1) {
169 fetch_syserr(); 169 fetch_syserr();
170 return (-1); 170 return (-1);
171 } 171 }
172 if (isftpinfo(conn->buf)) { 172 if (isftpinfo(conn->buf)) {
173 while (conn->buflen && !isftpreply(conn->buf)) { 173 while (conn->buflen && !isftpreply(conn->buf)) {
174 if (fetch_getln(conn) == -1) { 174 if (fetch_getln(conn) == -1) {
175 fetch_syserr(); 175 fetch_syserr();
176 return (-1); 176 return (-1);
177 } 177 }
178 } 178 }
179 } 179 }
180 180
181 while (conn->buflen && 181 while (conn->buflen &&
182 isspace((unsigned char)conn->buf[conn->buflen - 1])) 182 isspace((unsigned char)conn->buf[conn->buflen - 1]))
183 conn->buflen--; 183 conn->buflen--;
184 conn->buf[conn->buflen] = '\0'; 184 conn->buf[conn->buflen] = '\0';
185 185
186 if (!isftpreply(conn->buf)) { 186 if (!isftpreply(conn->buf)) {
187 ftp_seterr(FTP_PROTOCOL_ERROR); 187 ftp_seterr(FTP_PROTOCOL_ERROR);
188 return (-1); 188 return (-1);
189 } 189 }
190 190
191 conn->err = (conn->buf[0] - '0') * 100 191 conn->err = (conn->buf[0] - '0') * 100
192 + (conn->buf[1] - '0') * 10 192 + (conn->buf[1] - '0') * 10
193 + (conn->buf[2] - '0'); 193 + (conn->buf[2] - '0');
194 194
195 return (conn->err); 195 return (conn->err);
196} 196}
197 197
198/* 198/*
199 * Send a command and check reply 199 * Send a command and check reply
200 */ 200 */
 201__printflike(2, 3)
201static int 202static int
202ftp_cmd(conn_t *conn, const char *fmt, ...) 203ftp_cmd(conn_t *conn, const char *fmt, ...)
203{ 204{
204 va_list ap; 205 va_list ap;
205 size_t len; 206 size_t len;
206 char *msg; 207 char *msg;
207 ssize_t r; 208 ssize_t r;
208 209
209 va_start(ap, fmt); 210 va_start(ap, fmt);
210 len = vasprintf(&msg, fmt, ap); 211 len = vasprintf(&msg, fmt, ap);
211 va_end(ap); 212 va_end(ap);
212 213
213 if (msg == NULL) { 214 if (msg == NULL) {
214 errno = ENOMEM; 215 errno = ENOMEM;
215 fetch_syserr(); 216 fetch_syserr();
216 return (-1); 217 return (-1);
217 } 218 }
218 219
219 r = fetch_write(conn, msg, len); 220 r = fetch_write(conn, msg, len);
220 free(msg); 221 free(msg);
221 222
222 if (r == -1) { 223 if (r == -1) {
223 fetch_syserr(); 224 fetch_syserr();
224 return (-1); 225 return (-1);
225 } 226 }
226 227
227 return (ftp_chkerr(conn)); 228 return (ftp_chkerr(conn));
228} 229}
229 230
230/* 231/*
231 * Return a pointer to the filename part of a path 232 * Return a pointer to the filename part of a path
232 */ 233 */
233static const char * 234static const char *
234ftp_filename(const char *file, size_t *len, int *type, int subdir) 235ftp_filename(const char *file, size_t *len, int *type, int subdir)
235{ 236{
236 const char *s; 237 const char *s;
237 238
238 if ((s = strrchr(file, '/')) == NULL || subdir) 239 if ((s = strrchr(file, '/')) == NULL || subdir)
239 s = file; 240 s = file;
240 else 241 else
241 s = s + 1; 242 s = s + 1;
242 *len = strlen(s); 243 *len = strlen(s);
243 if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { 244 if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) {
244 *type = s[*len - 1]; 245 *type = s[*len - 1];
245 *len -= 7; 246 *len -= 7;
246 } else { 247 } else {
247 *type = '\0'; 248 *type = '\0';
248 } 249 }
249 return (s); 250 return (s);
250} 251}
251 252
252/* 253/*
253 * Get current working directory from the reply to a CWD, PWD or CDUP 254 * Get current working directory from the reply to a CWD, PWD or CDUP
254 * command. 255 * command.
255 */ 256 */
256static int 257static int
257ftp_pwd(conn_t *conn, char **pwd) 258ftp_pwd(conn_t *conn, char **pwd)
258{ 259{
259 char *src, *dst, *end; 260 char *src, *dst, *end;
260 int q; 261 int q;
261 size_t len; 262 size_t len;
262 263
263 if (conn->err != FTP_WORKING_DIRECTORY && 264 if (conn->err != FTP_WORKING_DIRECTORY &&
264 conn->err != FTP_FILE_ACTION_OK) 265 conn->err != FTP_FILE_ACTION_OK)
265 return (FTP_PROTOCOL_ERROR); 266 return (FTP_PROTOCOL_ERROR);
266 end = conn->buf + conn->buflen; 267 end = conn->buf + conn->buflen;
267 src = conn->buf + 4; 268 src = conn->buf + 4;
268 if (src >= end || *src++ != '"') 269 if (src >= end || *src++ != '"')
269 return (FTP_PROTOCOL_ERROR); 270 return (FTP_PROTOCOL_ERROR);
270 len = end - src + 1; 271 len = end - src + 1;
271 *pwd = malloc(len); 272 *pwd = malloc(len);
272 if (*pwd == NULL) 273 if (*pwd == NULL)
273 return (FTP_PROTOCOL_ERROR); 274 return (FTP_PROTOCOL_ERROR);
274 for (q = 0, dst = *pwd; src < end; ++src) { 275 for (q = 0, dst = *pwd; src < end; ++src) {
275 if (!q && *src == '"') 276 if (!q && *src == '"')
276 q = 1; 277 q = 1;
277 else if (q && *src != '"') 278 else if (q && *src != '"')
278 break; 279 break;
279 else if (q) 280 else if (q)
280 *dst++ = '"', q = 0; 281 *dst++ = '"', q = 0;
281 else 282 else
282 *dst++ = *src; 283 *dst++ = *src;
283 } 284 }
284 *dst = '\0'; 285 *dst = '\0';
285 if (**pwd != '/') { 286 if (**pwd != '/') {
286 free(*pwd); 287 free(*pwd);
287 *pwd = NULL; 288 *pwd = NULL;
288 return (FTP_PROTOCOL_ERROR); 289 return (FTP_PROTOCOL_ERROR);
289 } 290 }
290 return (FTP_OK); 291 return (FTP_OK);
291} 292}
292 293
293/* 294/*
294 * Change working directory to the directory that contains the specified 295 * Change working directory to the directory that contains the specified
295 * file. 296 * file.
296 */ 297 */
297static int 298static int
298ftp_cwd(conn_t *conn, const char *path, int subdir) 299ftp_cwd(conn_t *conn, const char *path, int subdir)
299{ 300{
300 const char *beg, *end; 301 const char *beg, *end;
301 char *pwd, *dst; 302 char *pwd, *dst;
302 int e; 303 int e;
303 size_t i, len; 304 size_t i, len;
304 305
305 if (*path != '/') { 306 if (*path != '/') {
306 ftp_seterr(501); 307 ftp_seterr(501);
307 return (-1); 308 return (-1);
308 } 309 }
309 ++path; 310 ++path;
310 311
311 /* Simple case: still in the home directory and no directory change. */ 312 /* Simple case: still in the home directory and no directory change. */
312 if (conn->ftp_home == NULL && strchr(path, '/') == NULL && 313 if (conn->ftp_home == NULL && strchr(path, '/') == NULL &&
313 (!subdir || *path == '\0')) 314 (!subdir || *path == '\0'))
314 return 0; 315 return 0;
315 316
316 if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || 317 if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
317 (e = ftp_pwd(conn, &pwd)) != FTP_OK) { 318 (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
318 ftp_seterr(e); 319 ftp_seterr(e);
319 return (-1); 320 return (-1);
320 } 321 }
321 if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) { 322 if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) {
322 fetch_syserr(); 323 fetch_syserr();
323 free(pwd); 324 free(pwd);
324 return (-1); 325 return (-1);
325 } 326 }
326 if (*path == '/') { 327 if (*path == '/') {
327 while (path[1] == '/') 328 while (path[1] == '/')
328 ++path; 329 ++path;
329 dst = strdup(path); 330 dst = strdup(path);
330 } else if (strcmp(conn->ftp_home, "/") == 0) { 331 } else if (strcmp(conn->ftp_home, "/") == 0) {
331 dst = strdup(path - 1); 332 dst = strdup(path - 1);
332 } else { 333 } else {
333 asprintf(&dst, "%s/%s", conn->ftp_home, path); 334 asprintf(&dst, "%s/%s", conn->ftp_home, path);
334 } 335 }
335 if (dst == NULL) { 336 if (dst == NULL) {
336 fetch_syserr(); 337 fetch_syserr();
337 free(pwd); 338 free(pwd);
338 return (-1); 339 return (-1);
339 } 340 }
340 341
341 if (subdir) 342 if (subdir)
342 end = dst + strlen(dst); 343 end = dst + strlen(dst);
343 else 344 else
344 end = strrchr(dst, '/'); 345 end = strrchr(dst, '/');
345 346
346 for (;;) { 347 for (;;) {
347 len = strlen(pwd); 348 len = strlen(pwd);
348 349
349 /* Look for a common prefix between PWD and dir to fetch. */ 350 /* Look for a common prefix between PWD and dir to fetch. */
350 for (i = 0; i <= len && i <= (size_t)(end - dst); ++i) 351 for (i = 0; i <= len && i <= (size_t)(end - dst); ++i)
351 if (pwd[i] != dst[i]) 352 if (pwd[i] != dst[i])
352 break; 353 break;
353 /* Keep going up a dir until we have a matching prefix. */ 354 /* Keep going up a dir until we have a matching prefix. */
354 if (strcmp(pwd, "/") == 0) 355 if (strcmp(pwd, "/") == 0)
355 break; 356 break;
356 if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/')) 357 if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/'))
357 break; 358 break;
358 free(pwd); 359 free(pwd);
359 if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK || 360 if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK ||
360 (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || 361 (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
361 (e = ftp_pwd(conn, &pwd)) != FTP_OK) { 362 (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
362 ftp_seterr(e); 363 ftp_seterr(e);
363 free(dst); 364 free(dst);
364 return (-1); 365 return (-1);
365 } 366 }
366 } 367 }
367 free(pwd); 368 free(pwd);
368 369
369#ifdef FTP_COMBINE_CWDS 370#ifdef FTP_COMBINE_CWDS
370 /* Skip leading slashes, even "////". */ 371 /* Skip leading slashes, even "////". */
371 for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i) 372 for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i)
372 /* nothing */ ; 373 /* nothing */ ;
373 374
374 /* If there is no trailing dir, we're already there. */ 375 /* If there is no trailing dir, we're already there. */
375 if (beg >= end) { 376 if (beg >= end) {
376 free(dst); 377 free(dst);
377 return (0); 378 return (0);
378 } 379 }
379 380
380 /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ 381 /* Change to the directory all in one chunk (e.g., foo/bar/baz). */
381 e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg); 382 e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg);
382 if (e == FTP_FILE_ACTION_OK) { 383 if (e == FTP_FILE_ACTION_OK) {
383 free(dst); 384 free(dst);
384 return (0); 385 return (0);
385 } 386 }
386#endif /* FTP_COMBINE_CWDS */ 387#endif /* FTP_COMBINE_CWDS */
387 388
388 /* That didn't work so go back to legacy behavior (multiple CWDs). */ 389 /* That didn't work so go back to legacy behavior (multiple CWDs). */
389 for (beg = dst + i; beg < end; beg = dst + i + 1) { 390 for (beg = dst + i; beg < end; beg = dst + i + 1) {
390 while (*beg == '/') 391 while (*beg == '/')
391 ++beg, ++i; 392 ++beg, ++i;
392 for (++i; dst + i < end && dst[i] != '/'; ++i) 393 for (++i; dst + i < end && dst[i] != '/'; ++i)
393 /* nothing */ ; 394 /* nothing */ ;
394 e = ftp_cmd(conn, "CWD %.*s\r\n", dst + i - beg, beg); 395 e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(dst + i - beg), beg);
395 if (e != FTP_FILE_ACTION_OK) { 396 if (e != FTP_FILE_ACTION_OK) {
396 free(dst); 397 free(dst);
397 ftp_seterr(e); 398 ftp_seterr(e);
398 return (-1); 399 return (-1);
399 } 400 }
400 } 401 }
401 free(dst); 402 free(dst);
402 return (0); 403 return (0);
403} 404}
404 405
405/* 406/*
406 * Set transfer mode and data type 407 * Set transfer mode and data type
407 */ 408 */
408static int 409static int
409ftp_mode_type(conn_t *conn, int mode, int type) 410ftp_mode_type(conn_t *conn, int mode, int type)
410{ 411{
411 int e; 412 int e;
412 413
413 switch (mode) { 414 switch (mode) {
414 case 0: 415 case 0:
415 case 's': 416 case 's':
416 mode = 'S'; 417 mode = 'S';
417 /*FALLTHROUGH*/ 418 /*FALLTHROUGH*/
418 case 'S': 419 case 'S':
419 break; 420 break;
420 default: 421 default:
421 return (FTP_PROTOCOL_ERROR); 422 return (FTP_PROTOCOL_ERROR);
422 } 423 }
423 if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) { 424 if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) {
424 if (mode == 'S') { 425 if (mode == 'S') {
425 /* 426 /*
426 * Stream mode is supposed to be the default - so 427 * Stream mode is supposed to be the default - so
427 * much so that some servers not only do not 428 * much so that some servers not only do not
428 * support any other mode, but do not support the 429 * support any other mode, but do not support the
429 * MODE command at all. 430 * MODE command at all.
430 * 431 *
431 * If "MODE S" fails, it is unlikely that we 432 * If "MODE S" fails, it is unlikely that we
432 * previously succeeded in setting a different 433 * previously succeeded in setting a different
433 * mode. Therefore, we simply hope that the 434 * mode. Therefore, we simply hope that the
434 * server is already in the correct mode, and 435 * server is already in the correct mode, and
435 * silently ignore the failure. 436 * silently ignore the failure.
436 */ 437 */
437 } else { 438 } else {
438 return (e); 439 return (e);
439 } 440 }
440 } 441 }
441 442
442 switch (type) { 443 switch (type) {
443 case 0: 444 case 0:
444 case 'i': 445 case 'i':
445 type = 'I'; 446 type = 'I';
446 /*FALLTHROUGH*/ 447 /*FALLTHROUGH*/
447 case 'I': 448 case 'I':
448 break; 449 break;
449 case 'a': 450 case 'a':
450 type = 'A'; 451 type = 'A';
451 /*FALLTHROUGH*/ 452 /*FALLTHROUGH*/
452 case 'A': 453 case 'A':
453 break; 454 break;
454 case 'd': 455 case 'd':
455 type = 'D'; 456 type = 'D';
456 /*FALLTHROUGH*/ 457 /*FALLTHROUGH*/
457 case 'D': 458 case 'D':
458 /* can't handle yet */ 459 /* can't handle yet */
459 default: 460 default:
460 return (FTP_PROTOCOL_ERROR); 461 return (FTP_PROTOCOL_ERROR);
461 } 462 }
462 if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK) 463 if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK)
463 return (e); 464 return (e);
464 465
465 return (FTP_OK); 466 return (FTP_OK);
466} 467}
467 468
468/* 469/*
469 * Request and parse file stats 470 * Request and parse file stats
470 */ 471 */
471static int 472static int
472ftp_stat(conn_t *conn, const char *file, struct url_stat *us) 473ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
473{ 474{
474 char *ln; 475 char *ln;
475 const char *filename; 476 const char *filename;
476 size_t filenamelen; 477 size_t filenamelen;
477 int type; 478 int type;
478 struct tm tm; 479 struct tm tm;
479 time_t t; 480 time_t t;
480 int e; 481 int e;
481 482
482 us->size = -1; 483 us->size = -1;
483 us->atime = us->mtime = 0; 484 us->atime = us->mtime = 0;
484 485
485 filename = ftp_filename(file, &filenamelen, &type, 0); 486 filename = ftp_filename(file, &filenamelen, &type, 0);
486 487
487 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { 488 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) {
488 ftp_seterr(e); 489 ftp_seterr(e);
489 return (-1); 490 return (-1);
490 } 491 }
491 492
492 e = ftp_cmd(conn, "SIZE %.*s\r\n", filenamelen, filename); 493 e = ftp_cmd(conn, "SIZE %.*s\r\n", (int)filenamelen, filename);
493 if (e != FTP_FILE_STATUS) { 494 if (e != FTP_FILE_STATUS) {
494 ftp_seterr(e); 495 ftp_seterr(e);
495 return (-1); 496 return (-1);
496 } 497 }
497 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) 498 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
498 /* nothing */ ; 499 /* nothing */ ;
499 for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) 500 for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
500 us->size = us->size * 10 + *ln - '0'; 501 us->size = us->size * 10 + *ln - '0';
501 if (*ln && !isspace((unsigned char)*ln)) { 502 if (*ln && !isspace((unsigned char)*ln)) {
502 ftp_seterr(FTP_PROTOCOL_ERROR); 503 ftp_seterr(FTP_PROTOCOL_ERROR);
503 us->size = -1; 504 us->size = -1;
504 return (-1); 505 return (-1);
505 } 506 }
506 if (us->size == 0) 507 if (us->size == 0)
507 us->size = -1; 508 us->size = -1;
508 509
509 e = ftp_cmd(conn, "MDTM %.*s\r\n", filenamelen, filename); 510 e = ftp_cmd(conn, "MDTM %.*s\r\n", (int)filenamelen, filename);
510 if (e != FTP_FILE_STATUS) { 511 if (e != FTP_FILE_STATUS) {
511 ftp_seterr(e); 512 ftp_seterr(e);
512 return (-1); 513 return (-1);
513 } 514 }
514 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) 515 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
515 /* nothing */ ; 516 /* nothing */ ;
516 switch (strspn(ln, "0123456789")) { 517 switch (strspn(ln, "0123456789")) {
517 case 14: 518 case 14:
518 break; 519 break;
519 case 15: 520 case 15:
520 ln++; 521 ln++;
521 ln[0] = '2'; 522 ln[0] = '2';
522 ln[1] = '0'; 523 ln[1] = '0';
523 break; 524 break;
524 default: 525 default:
525 ftp_seterr(FTP_PROTOCOL_ERROR); 526 ftp_seterr(FTP_PROTOCOL_ERROR);
526 return (-1); 527 return (-1);
527 } 528 }
528 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 529 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
529 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 530 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
530 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 531 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
531 ftp_seterr(FTP_PROTOCOL_ERROR); 532 ftp_seterr(FTP_PROTOCOL_ERROR);
532 return (-1); 533 return (-1);
533 } 534 }
534 tm.tm_mon--; 535 tm.tm_mon--;
535 tm.tm_year -= 1900; 536 tm.tm_year -= 1900;
536 tm.tm_isdst = -1; 537 tm.tm_isdst = -1;
537 t = timegm(&tm); 538 t = timegm(&tm);
538 if (t == (time_t)-1) 539 if (t == (time_t)-1)
539 t = time(NULL); 540 t = time(NULL);
540 us->mtime = t; 541 us->mtime = t;
541 us->atime = t; 542 us->atime = t;
542 543
543 return (0); 544 return (0);
544} 545}
545 546
546/* 547/*
547 * I/O functions for FTP 548 * I/O functions for FTP
548 */ 549 */
549struct ftpio { 550struct ftpio {
550 conn_t *cconn; /* Control connection */ 551 conn_t *cconn; /* Control connection */
551 conn_t *dconn; /* Data connection */ 552 conn_t *dconn; /* Data connection */
552 int dir; /* Direction */ 553 int dir; /* Direction */
553 int eof; /* EOF reached */ 554 int eof; /* EOF reached */
554 int err; /* Error code */ 555 int err; /* Error code */
555}; 556};
556 557
557static ssize_t ftp_readfn(void *, void *, size_t); 558static ssize_t ftp_readfn(void *, void *, size_t);
558static ssize_t ftp_writefn(void *, const void *, size_t); 559static ssize_t ftp_writefn(void *, const void *, size_t);
559static void ftp_closefn(void *); 560static void ftp_closefn(void *);
560 561
561static ssize_t 562static ssize_t
562ftp_readfn(void *v, void *buf, size_t len) 563ftp_readfn(void *v, void *buf, size_t len)
563{ 564{
564 struct ftpio *io; 565 struct ftpio *io;
565 ssize_t r; 566 ssize_t r;
566 567
567 io = (struct ftpio *)v; 568 io = (struct ftpio *)v;
568 if (io == NULL) { 569 if (io == NULL) {
569 errno = EBADF; 570 errno = EBADF;
570 return (-1); 571 return (-1);
571 } 572 }
572 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { 573 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
573 errno = EBADF; 574 errno = EBADF;
574 return (-1); 575 return (-1);
575 } 576 }
576 if (io->err) { 577 if (io->err) {
577 errno = io->err; 578 errno = io->err;
578 return (-1); 579 return (-1);
579 } 580 }
580 if (io->eof) 581 if (io->eof)
581 return (0); 582 return (0);
582 r = fetch_read(io->dconn, buf, len); 583 r = fetch_read(io->dconn, buf, len);
583 if (r > 0) 584 if (r > 0)
584 return (r); 585 return (r);
585 if (r == 0) { 586 if (r == 0) {
586 io->eof = 1; 587 io->eof = 1;
587 return (0); 588 return (0);
588 } 589 }
589 if (errno != EINTR) 590 if (errno != EINTR)
590 io->err = errno; 591 io->err = errno;
591 return (-1); 592 return (-1);
592} 593}
593 594
594static ssize_t 595static ssize_t
595ftp_writefn(void *v, const void *buf, size_t len) 596ftp_writefn(void *v, const void *buf, size_t len)
596{ 597{
597 struct ftpio *io; 598 struct ftpio *io;
598 ssize_t w; 599 ssize_t w;
599 600
600 io = (struct ftpio *)v; 601 io = (struct ftpio *)v;
601 if (io == NULL) { 602 if (io == NULL) {
602 errno = EBADF; 603 errno = EBADF;
603 return (-1); 604 return (-1);
604 } 605 }
605 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { 606 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
606 errno = EBADF; 607 errno = EBADF;
607 return (-1); 608 return (-1);
608 } 609 }
609 if (io->err) { 610 if (io->err) {
610 errno = io->err; 611 errno = io->err;
611 return (-1); 612 return (-1);
612 } 613 }
613 w = fetch_write(io->dconn, buf, len); 614 w = fetch_write(io->dconn, buf, len);
614 if (w >= 0) 615 if (w >= 0)
615 return (w); 616 return (w);
616 if (errno != EINTR) 617 if (errno != EINTR)
617 io->err = errno; 618 io->err = errno;
618 return (-1); 619 return (-1);
619} 620}
620 621
621static int 622static int
622ftp_disconnect(conn_t *conn) 623ftp_disconnect(conn_t *conn)
623{ 624{
624 ftp_cmd(conn, "QUIT\r\n"); 625 ftp_cmd(conn, "QUIT\r\n");
625 return fetch_close(conn); 626 return fetch_close(conn);
626} 627}
627 628
628static void 629static void
629ftp_closefn(void *v) 630ftp_closefn(void *v)
630{ 631{
631 struct ftpio *io; 632 struct ftpio *io;
632 633
633 io = (struct ftpio *)v; 634 io = (struct ftpio *)v;
634 if (io == NULL) { 635 if (io == NULL) {
635 errno = EBADF; 636 errno = EBADF;
636 return; 637 return;
637 } 638 }
638 if (io->dir == -1) 639 if (io->dir == -1)
639 return; 640 return;
640 if (io->cconn == NULL || io->dconn == NULL) { 641 if (io->cconn == NULL || io->dconn == NULL) {
641 errno = EBADF; 642 errno = EBADF;
642 return; 643 return;
643 } 644 }
644 fetch_close(io->dconn); 645 fetch_close(io->dconn);
645 io->dconn = NULL; 646 io->dconn = NULL;
646 io->dir = -1; 647 io->dir = -1;
647 (void)ftp_chkerr(io->cconn); 648 (void)ftp_chkerr(io->cconn);
648 fetch_cache_put(io->cconn, ftp_disconnect); 649 fetch_cache_put(io->cconn, ftp_disconnect);
649 free(io); 650 free(io);
650 return; 651 return;
651} 652}
652 653
653static fetchIO * 654static fetchIO *
654ftp_setup(conn_t *cconn, conn_t *dconn, int mode) 655ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
655{ 656{
656 struct ftpio *io; 657 struct ftpio *io;
657 fetchIO *f; 658 fetchIO *f;
658 659
659 if (cconn == NULL || dconn == NULL) 660 if (cconn == NULL || dconn == NULL)
660 return (NULL); 661 return (NULL);
661 if ((io = malloc(sizeof(*io))) == NULL) 662 if ((io = malloc(sizeof(*io))) == NULL)
662 return (NULL); 663 return (NULL);
663 io->cconn = cconn; 664 io->cconn = cconn;
664 io->dconn = dconn; 665 io->dconn = dconn;
665 io->dir = mode; 666 io->dir = mode;
666 io->eof = io->err = 0; 667 io->eof = io->err = 0;
667 f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); 668 f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn);
668 if (f == NULL) 669 if (f == NULL)
669 free(io); 670 free(io);
670 return (f); 671 return (f);
671} 672}
672 673
673/* 674/*
674 * Transfer file 675 * Transfer file
675 */ 676 */
676static fetchIO * 677static fetchIO *
677ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, 678ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg,
678 int mode, off_t offset, const char *flags) 679 int mode, off_t offset, const char *flags)
679{ 680{
680 union anonymous { 681 union anonymous {
681 struct sockaddr_storage ss; 682 struct sockaddr_storage ss;
682 struct sockaddr sa; 683 struct sockaddr sa;
683 struct sockaddr_in6 sin6; 684 struct sockaddr_in6 sin6;
684 struct sockaddr_in sin4; 685 struct sockaddr_in sin4;
685 } u; 686 } u;
686 const char *bindaddr; 687 const char *bindaddr;
687 const char *filename; 688 const char *filename;
688 size_t filenamelen; 689 size_t filenamelen;
689 int type; 690 int type;
690 int low, pasv, verbose; 691 int low, pasv, verbose;
691 int e, sd = -1; 692 int e, sd = -1;
692 socklen_t l; 693 socklen_t l;
693 char *s; 694 char *s;
694 fetchIO *df; 695 fetchIO *df;
695 696
696 /* check flags */ 697 /* check flags */
697 low = CHECK_FLAG('l'); 698 low = CHECK_FLAG('l');
698 pasv = !CHECK_FLAG('a'); 699 pasv = !CHECK_FLAG('a');
699 verbose = CHECK_FLAG('v'); 700 verbose = CHECK_FLAG('v');
700 701
701 /* passive mode */ 702 /* passive mode */
702 if (!pasv) 703 if (!pasv)
703 pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && 704 pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
704 strncasecmp(s, "no", 2) != 0); 705 strncasecmp(s, "no", 2) != 0);
705 706
706 /* isolate filename */ 707 /* isolate filename */
707 filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); 708 filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL);
708 709
709 /* set transfer mode and data type */ 710 /* set transfer mode and data type */
710 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) 711 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK)
711 goto ouch; 712 goto ouch;
712 713
713 /* find our own address, bind, and listen */ 714 /* find our own address, bind, and listen */
714 l = sizeof(u.ss); 715 l = sizeof(u.ss);
715 if (getsockname(conn->sd, &u.sa, &l) == -1) 716 if (getsockname(conn->sd, &u.sa, &l) == -1)
716 goto sysouch; 717 goto sysouch;
717 if (u.ss.ss_family == AF_INET6) 718 if (u.ss.ss_family == AF_INET6)
718 unmappedaddr(&u.sin6, &l); 719 unmappedaddr(&u.sin6, &l);
719 720
720retry_mode: 721retry_mode:
721 722
722 /* open data socket */ 723 /* open data socket */
723 if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 724 if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
724 fetch_syserr(); 725 fetch_syserr();
725 return (NULL); 726 return (NULL);
726 } 727 }
727 728
728 if (pasv) { 729 if (pasv) {
729 unsigned char addr[64]; 730 unsigned char addr[64];
730 char *ln, *p; 731 char *ln, *p;
731 unsigned int i; 732 unsigned int i;
732 int port; 733 int port;
733 734
734 /* send PASV command */ 735 /* send PASV command */
735 if (verbose) 736 if (verbose)
736 fetch_info("setting passive mode"); 737 fetch_info("setting passive mode");
737 switch (u.ss.ss_family) { 738 switch (u.ss.ss_family) {
738 case AF_INET: 739 case AF_INET:
739 if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE) 740 if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE)
740 goto ouch; 741 goto ouch;
741 break; 742 break;
742 case AF_INET6: 743 case AF_INET6:
743 if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) { 744 if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) {
744 if (e == -1) 745 if (e == -1)
745 goto ouch; 746 goto ouch;
746 if ((e = ftp_cmd(conn, "LPSV\r\n")) != 747 if ((e = ftp_cmd(conn, "LPSV\r\n")) !=
747 FTP_LPASSIVE_MODE) 748 FTP_LPASSIVE_MODE)
748 goto ouch; 749 goto ouch;
749 } 750 }
750 break; 751 break;
751 default: 752 default:
752 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 753 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
753 goto ouch; 754 goto ouch;
754 } 755 }
755 756
756 /* 757 /*
757 * Find address and port number. The reply to the PASV command 758 * Find address and port number. The reply to the PASV command
758 * is IMHO the one and only weak point in the FTP protocol. 759 * is IMHO the one and only weak point in the FTP protocol.
759 */ 760 */
760 ln = conn->buf; 761 ln = conn->buf;
761 switch (e) { 762 switch (e) {
762 case FTP_PASSIVE_MODE: 763 case FTP_PASSIVE_MODE:
763 case FTP_LPASSIVE_MODE: 764 case FTP_LPASSIVE_MODE:
764 for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) 765 for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
765 /* nothing */ ; 766 /* nothing */ ;
766 if (!*p) { 767 if (!*p) {
767 e = FTP_PROTOCOL_ERROR; 768 e = FTP_PROTOCOL_ERROR;
768 goto ouch; 769 goto ouch;
769 } 770 }
770 l = (e == FTP_PASSIVE_MODE ? 6 : 21); 771 l = (e == FTP_PASSIVE_MODE ? 6 : 21);
771 for (i = 0; *p && i < l; i++, p++) 772 for (i = 0; *p && i < l; i++, p++)
772 addr[i] = (unsigned char)strtol(p, &p, 10); 773 addr[i] = (unsigned char)strtol(p, &p, 10);
773 if (i < l) { 774 if (i < l) {
774 e = FTP_PROTOCOL_ERROR; 775 e = FTP_PROTOCOL_ERROR;
775 goto ouch; 776 goto ouch;
776 } 777 }
777 break; 778 break;
778 case FTP_EPASSIVE_MODE: 779 case FTP_EPASSIVE_MODE:
779 for (p = ln + 3; *p && *p != '('; p++) 780 for (p = ln + 3; *p && *p != '('; p++)
780 /* nothing */ ; 781 /* nothing */ ;
781 if (!*p) { 782 if (!*p) {
782 e = FTP_PROTOCOL_ERROR; 783 e = FTP_PROTOCOL_ERROR;
783 goto ouch; 784 goto ouch;
784 } 785 }
785 ++p; 786 ++p;
786 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 787 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
787 &port, &addr[3]) != 5 || 788 &port, &addr[3]) != 5 ||
788 addr[0] != addr[1] || 789 addr[0] != addr[1] ||
789 addr[0] != addr[2] || addr[0] != addr[3]) { 790 addr[0] != addr[2] || addr[0] != addr[3]) {
790 e = FTP_PROTOCOL_ERROR; 791 e = FTP_PROTOCOL_ERROR;
791 goto ouch; 792 goto ouch;
792 } 793 }
793 break; 794 break;
794 case FTP_SYNTAX_ERROR: 795 case FTP_SYNTAX_ERROR:
795 if (verbose) 796 if (verbose)
796 fetch_info("passive mode failed"); 797 fetch_info("passive mode failed");
797 /* Close socket and retry with passive mode. */ 798 /* Close socket and retry with passive mode. */
798 pasv = 0; 799 pasv = 0;
799 close(sd); 800 close(sd);
800 sd = -1; 801 sd = -1;
801 goto retry_mode; 802 goto retry_mode;
802 } 803 }
803 804
804 /* seek to required offset */ 805 /* seek to required offset */
805 if (offset) 806 if (offset)
806 if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK) 807 if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK)
807 goto sysouch; 808 goto sysouch;
808 809
809 /* construct sockaddr for data socket */ 810 /* construct sockaddr for data socket */
810 l = sizeof(u.ss); 811 l = sizeof(u.ss);
811 if (getpeername(conn->sd, &u.sa, &l) == -1) 812 if (getpeername(conn->sd, &u.sa, &l) == -1)
812 goto sysouch; 813 goto sysouch;
813 if (u.ss.ss_family == AF_INET6) 814 if (u.ss.ss_family == AF_INET6)
814 unmappedaddr(&u.sin6, &l); 815 unmappedaddr(&u.sin6, &l);
815 switch (u.ss.ss_family) { 816 switch (u.ss.ss_family) {
816 case AF_INET6: 817 case AF_INET6:
817 if (e == FTP_EPASSIVE_MODE) 818 if (e == FTP_EPASSIVE_MODE)
818 u.sin6.sin6_port = htons(port); 819 u.sin6.sin6_port = htons(port);
819 else { 820 else {
820 memcpy(&u.sin6.sin6_addr, addr + 2, 16); 821 memcpy(&u.sin6.sin6_addr, addr + 2, 16);
821 memcpy(&u.sin6.sin6_port, addr + 19, 2); 822 memcpy(&u.sin6.sin6_port, addr + 19, 2);
822 } 823 }
823 break; 824 break;
824 case AF_INET: 825 case AF_INET:
825 if (e == FTP_EPASSIVE_MODE) 826 if (e == FTP_EPASSIVE_MODE)
826 u.sin4.sin_port = htons(port); 827 u.sin4.sin_port = htons(port);
827 else { 828 else {
828 memcpy(&u.sin4.sin_addr, addr, 4); 829 memcpy(&u.sin4.sin_addr, addr, 4);
829 memcpy(&u.sin4.sin_port, addr + 4, 2); 830 memcpy(&u.sin4.sin_port, addr + 4, 2);
830 } 831 }
831 break; 832 break;
832 default: 833 default:
833 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 834 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
834 break; 835 break;
835 } 836 }
836 837
837 /* connect to data port */ 838 /* connect to data port */
838 if (verbose) 839 if (verbose)
839 fetch_info("opening data connection"); 840 fetch_info("opening data connection");
840 bindaddr = getenv("FETCH_BIND_ADDRESS"); 841 bindaddr = getenv("FETCH_BIND_ADDRESS");
841 if (bindaddr != NULL && *bindaddr != '\0' && 842 if (bindaddr != NULL && *bindaddr != '\0' &&
842 fetch_bind(sd, u.ss.ss_family, bindaddr) != 0) 843 fetch_bind(sd, u.ss.ss_family, bindaddr) != 0)
843 goto sysouch; 844 goto sysouch;
844 if (connect(sd, &u.sa, l) == -1) 845 if (connect(sd, &u.sa, l) == -1)
845 goto sysouch; 846 goto sysouch;
846 847
847 /* make the server initiate the transfer */ 848 /* make the server initiate the transfer */
848 if (verbose) 849 if (verbose)
849 fetch_info("initiating transfer"); 850 fetch_info("initiating transfer");
850 if (op_arg) 851 if (op_arg)
851 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); 852 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
852 else 853 else
853 e = ftp_cmd(conn, "%s %.*s\r\n", oper, 854 e = ftp_cmd(conn, "%s %.*s\r\n", oper,
854 filenamelen, filename); 855 (int)filenamelen, filename);
855 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 856 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
856 goto ouch; 857 goto ouch;
857 858
858 } else { 859 } else {
859 uint32_t a; 860 uint32_t a;
860 uint16_t p; 861 uint16_t p;
861#if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) 862#if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE)
862 int arg; 863 int arg;
863#endif 864#endif
864 int d; 865 int d;
865 char hname[INET6_ADDRSTRLEN]; 866 char hname[INET6_ADDRSTRLEN];
866 867
867 switch (u.ss.ss_family) { 868 switch (u.ss.ss_family) {
868 case AF_INET6: 869 case AF_INET6:
869 u.sin6.sin6_port = 0; 870 u.sin6.sin6_port = 0;
870#ifdef IPV6_PORTRANGE 871#ifdef IPV6_PORTRANGE
871 arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; 872 arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
872 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 873 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
873 &arg, (socklen_t)sizeof(arg)) == -1) 874 &arg, (socklen_t)sizeof(arg)) == -1)
874 goto sysouch; 875 goto sysouch;
875#endif 876#endif
876 break; 877 break;
877 case AF_INET: 878 case AF_INET:
878 u.sin4.sin_port = 0; 879 u.sin4.sin_port = 0;
879#ifdef IP_PORTRANGE 880#ifdef IP_PORTRANGE
880 arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; 881 arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
881 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 882 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
882 &arg, (socklen_t)sizeof(arg)) == -1) 883 &arg, (socklen_t)sizeof(arg)) == -1)
883 goto sysouch; 884 goto sysouch;
884#endif 885#endif
885 break; 886 break;
886 } 887 }
887 if (verbose) 888 if (verbose)
888 fetch_info("binding data socket"); 889 fetch_info("binding data socket");
889 if (bind(sd, &u.sa, l) == -1) 890 if (bind(sd, &u.sa, l) == -1)
890 goto sysouch; 891 goto sysouch;
891 if (listen(sd, 1) == -1) 892 if (listen(sd, 1) == -1)
892 goto sysouch; 893 goto sysouch;
893 894
894 /* find what port we're on and tell the server */ 895 /* find what port we're on and tell the server */
895 if (getsockname(sd, &u.sa, &l) == -1) 896 if (getsockname(sd, &u.sa, &l) == -1)
896 goto sysouch; 897 goto sysouch;
897 switch (u.ss.ss_family) { 898 switch (u.ss.ss_family) {
898 case AF_INET: 899 case AF_INET:
899 a = ntohl(u.sin4.sin_addr.s_addr); 900 a = ntohl(u.sin4.sin_addr.s_addr);
900 p = ntohs(u.sin4.sin_port); 901 p = ntohs(u.sin4.sin_port);
901 e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n", 902 e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n",
902 (a >> 24) & 0xff, (a >> 16) & 0xff, 903 (a >> 24) & 0xff, (a >> 16) & 0xff,
903 (a >> 8) & 0xff, a & 0xff, 904 (a >> 8) & 0xff, a & 0xff,
904 ((unsigned int)p >> 8) & 0xff, p & 0xff); 905 ((unsigned int)p >> 8) & 0xff, p & 0xff);
905 break; 906 break;
906 case AF_INET6: 907 case AF_INET6:
907 e = -1; 908 e = -1;
908 u.sin6.sin6_scope_id = 0; 909 u.sin6.sin6_scope_id = 0;
909 if (getnameinfo(&u.sa, l, 910 if (getnameinfo(&u.sa, l,
910 hname, (socklen_t)sizeof(hname), 911 hname, (socklen_t)sizeof(hname),
911 NULL, 0, NI_NUMERICHOST) == 0) { 912 NULL, 0, NI_NUMERICHOST) == 0) {
912 e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname, 913 e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname,
913 htons(u.sin6.sin6_port)); 914 htons(u.sin6.sin6_port));
914 if (e == -1) 915 if (e == -1)
915 goto ouch; 916 goto ouch;
916 } 917 }
917 if (e != FTP_OK) { 918 if (e != FTP_OK) {
918 uint8_t aa[sizeof(u.sin6.sin6_addr)]; 919 uint8_t aa[sizeof(u.sin6.sin6_addr)];
919 memcpy(aa, &u.sin6.sin6_addr, sizeof(aa)); 920 memcpy(aa, &u.sin6.sin6_addr, sizeof(aa));
920 p = ntohs(u.sin6.sin6_port); 921 p = ntohs(u.sin6.sin6_port);
921 e = ftp_cmd(conn, 922 e = ftp_cmd(conn,
922 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n", 923 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",
923 6, 16, 924 6, 16,
924 aa[ 0], aa[ 1], aa[ 2], aa[ 3], 925 aa[ 0], aa[ 1], aa[ 2], aa[ 3],
925 aa[ 4], aa[ 5], aa[ 6], aa[ 7], 926 aa[ 4], aa[ 5], aa[ 6], aa[ 7],
926 aa[ 8], aa[ 9], aa[10], aa[11], 927 aa[ 8], aa[ 9], aa[10], aa[11],
927 aa[12], aa[13], aa[14], aa[15], 928 aa[12], aa[13], aa[14], aa[15],
928 2, 929 2,
929 ((unsigned int)p >> 8) & 0xff, p & 0xff); 930 ((unsigned int)p >> 8) & 0xff, p & 0xff);
930 } 931 }
931 break; 932 break;
932 default: 933 default:
933 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 934 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
934 goto ouch; 935 goto ouch;
935 } 936 }
936 if (e != FTP_OK) 937 if (e != FTP_OK)
937 goto ouch; 938 goto ouch;
938 939
939 /* seek to required offset */ 940 /* seek to required offset */
940 if (offset) 941 if (offset)
941 if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK) 942 if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK)
942 goto sysouch; 943 goto sysouch;
943 944
944 /* make the server initiate the transfer */ 945 /* make the server initiate the transfer */
945 if (verbose) 946 if (verbose)
946 fetch_info("initiating transfer"); 947 fetch_info("initiating transfer");
947 if (op_arg) 948 if (op_arg)
948 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); 949 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
949 else 950 else
950 e = ftp_cmd(conn, "%s %.*s\r\n", oper, 951 e = ftp_cmd(conn, "%s %.*s\r\n", oper,
951 filenamelen, filename); 952 (int)filenamelen, filename);
952 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 953 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
953 goto ouch; 954 goto ouch;
954 955
955 /* accept the incoming connection and go to town */ 956 /* accept the incoming connection and go to town */
956 if ((d = accept(sd, NULL, NULL)) == -1) 957 if ((d = accept(sd, NULL, NULL)) == -1)
957 goto sysouch; 958 goto sysouch;
958 close(sd); 959 close(sd);
959 sd = d; 960 sd = d;
960 } 961 }
961 962
962 if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) 963 if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
963 goto sysouch; 964 goto sysouch;
964 return (df); 965 return (df);
965 966
966sysouch: 967sysouch:
967 fetch_syserr(); 968 fetch_syserr();
968 if (sd >= 0) 969 if (sd >= 0)
969 close(sd); 970 close(sd);
970 return (NULL); 971 return (NULL);
971 972
972ouch: 973ouch:
973 if (e != -1) 974 if (e != -1)
974 ftp_seterr(e); 975 ftp_seterr(e);
975 if (sd >= 0) 976 if (sd >= 0)
976 close(sd); 977 close(sd);
977 return (NULL); 978 return (NULL);
978} 979}
979 980
980/* 981/*
981 * Authenticate 982 * Authenticate
982 */ 983 */
983static int 984static int
984ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) 985ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
985{ 986{
986 const char *user, *pwd, *login_name; 987 const char *user, *pwd, *login_name;
987 char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; 988 char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1];
988 int e, len; 989 int e, len;
989 990
990 /* XXX FTP_AUTH, and maybe .netrc */ 991 /* XXX FTP_AUTH, and maybe .netrc */
991 992
992 /* send user name and password */ 993 /* send user name and password */
993 if (url->user[0] == '\0') 994 if (url->user[0] == '\0')
994 fetch_netrc_auth(url); 995 fetch_netrc_auth(url);
995 user = url->user; 996 user = url->user;
996 if (*user == '\0') 997 if (*user == '\0')
997 user = getenv("FTP_LOGIN"); 998 user = getenv("FTP_LOGIN");
998 if (user == NULL || *user == '\0') 999 if (user == NULL || *user == '\0')
999 user = FTP_ANONYMOUS_USER; 1000 user = FTP_ANONYMOUS_USER;
1000 if (purl && url->port == fetch_default_port(url->scheme)) 1001 if (purl && url->port == fetch_default_port(url->scheme))
1001 e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host); 1002 e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host);
1002 else if (purl) 1003 else if (purl)
1003 e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port); 1004 e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port);
1004 else 1005 else
1005 e = ftp_cmd(conn, "USER %s\r\n", user); 1006 e = ftp_cmd(conn, "USER %s\r\n", user);
1006 1007
1007 /* did the server request a password? */ 1008 /* did the server request a password? */
1008 if (e == FTP_NEED_PASSWORD) { 1009 if (e == FTP_NEED_PASSWORD) {
1009 pwd = url->pwd; 1010 pwd = url->pwd;
1010 if (*pwd == '\0') 1011 if (*pwd == '\0')
1011 pwd = getenv("FTP_PASSWORD"); 1012 pwd = getenv("FTP_PASSWORD");
1012 if (pwd == NULL || *pwd == '\0') { 1013 if (pwd == NULL || *pwd == '\0') {
1013 if ((login_name = getlogin()) == 0) 1014 if ((login_name = getlogin()) == 0)
1014 login_name = FTP_ANONYMOUS_USER; 1015 login_name = FTP_ANONYMOUS_USER;
1015 if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) 1016 if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0)
1016 len = 0; 1017 len = 0;
1017 else if (len > URL_USERLEN + 1) 1018 else if (len > URL_USERLEN + 1)
1018 len = URL_USERLEN + 1; 1019 len = URL_USERLEN + 1;
1019 gethostname(pbuf + len, sizeof(pbuf) - len); 1020 gethostname(pbuf + len, sizeof(pbuf) - len);
1020 /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ 1021 /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */
1021 pbuf[sizeof(pbuf) - 1] = '\0'; 1022 pbuf[sizeof(pbuf) - 1] = '\0';
1022 pwd = pbuf; 1023 pwd = pbuf;
1023 } 1024 }
1024 e = ftp_cmd(conn, "PASS %s\r\n", pwd); 1025 e = ftp_cmd(conn, "PASS %s\r\n", pwd);
1025 } 1026 }
1026 1027
1027 return (e); 1028 return (e);
1028} 1029}
1029 1030
1030/* 1031/*
1031 * Log on to FTP server 1032 * Log on to FTP server
1032 */ 1033 */
1033static conn_t * 1034static conn_t *
1034ftp_connect(struct url *url, struct url *purl, const char *flags) 1035ftp_connect(struct url *url, struct url *purl, const char *flags)
1035{ 1036{
1036 conn_t *conn; 1037 conn_t *conn;
1037 int e, direct, verbose; 1038 int e, direct, verbose;
1038#ifdef INET6 1039#ifdef INET6
1039 int af = AF_UNSPEC; 1040 int af = AF_UNSPEC;
1040#else 1041#else
1041 int af = AF_INET; 1042 int af = AF_INET;
1042#endif 1043#endif
1043 1044
1044 direct = CHECK_FLAG('d'); 1045 direct = CHECK_FLAG('d');
1045 verbose = CHECK_FLAG('v'); 1046 verbose = CHECK_FLAG('v');
1046 if (CHECK_FLAG('4')) 1047 if (CHECK_FLAG('4'))
1047 af = AF_INET; 1048 af = AF_INET;
1048 else if (CHECK_FLAG('6')) 1049 else if (CHECK_FLAG('6'))
1049 af = AF_INET6; 1050 af = AF_INET6;
1050 1051
1051 if (direct) 1052 if (direct)
1052 purl = NULL; 1053 purl = NULL;
1053 1054
1054 /* check for proxy */ 1055 /* check for proxy */
1055 if (purl) { 1056 if (purl) {
1056 /* XXX proxy authentication! */ 1057 /* XXX proxy authentication! */
1057 /* XXX connetion caching */ 1058 /* XXX connetion caching */
1058 if (!purl->port) 1059 if (!purl->port)
1059 purl->port = fetch_default_port(purl->scheme); 1060 purl->port = fetch_default_port(purl->scheme);
1060 1061
1061 conn = fetch_connect(purl, af, verbose); 1062 conn = fetch_connect(purl, af, verbose);
1062 } else { 1063 } else {
1063 /* no proxy, go straight to target */ 1064 /* no proxy, go straight to target */
1064 if (!url->port) 1065 if (!url->port)
1065 url->port = fetch_default_port(url->scheme); 1066 url->port = fetch_default_port(url->scheme);
1066 1067
1067 while ((conn = fetch_cache_get(url, af)) != NULL) { 1068 while ((conn = fetch_cache_get(url, af)) != NULL) {
1068 e = ftp_cmd(conn, "NOOP\r\n"); 1069 e = ftp_cmd(conn, "NOOP\r\n");
1069 if (e == FTP_OK) 1070 if (e == FTP_OK)
1070 return conn; 1071 return conn;
1071 fetch_close(conn); 1072 fetch_close(conn);
1072 } 1073 }
1073 conn = fetch_connect(url, af, verbose); 1074 conn = fetch_connect(url, af, verbose);
1074 purl = NULL; 1075 purl = NULL;
1075 } 1076 }
1076 1077
1077 /* check connection */ 1078 /* check connection */
1078 if (conn == NULL) 1079 if (conn == NULL)
1079 /* fetch_connect() has already set an error code */ 1080 /* fetch_connect() has already set an error code */
1080 return (NULL); 1081 return (NULL);
1081 1082
1082 /* expect welcome message */ 1083 /* expect welcome message */
1083 if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) 1084 if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
1084 goto fouch; 1085 goto fouch;
1085 1086
1086 /* authenticate */ 1087 /* authenticate */
1087 if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) 1088 if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
1088 goto fouch; 1089 goto fouch;
1089 1090
1090 /* TODO: Request extended features supported, if any (RFC 3659). */ 1091 /* TODO: Request extended features supported, if any (RFC 3659). */
1091 1092
1092 /* done */ 1093 /* done */
1093 return (conn); 1094 return (conn);
1094 1095
1095fouch: 1096fouch:
1096 if (e != -1) 1097 if (e != -1)
1097 ftp_seterr(e); 1098 ftp_seterr(e);
1098 fetch_close(conn); 1099 fetch_close(conn);
1099 return (NULL); 1100 return (NULL);
1100} 1101}
1101 1102
1102/* 1103/*
1103 * Check the proxy settings 1104 * Check the proxy settings
1104 */ 1105 */
1105static struct url * 1106static struct url *
1106ftp_get_proxy(struct url * url, const char *flags) 1107ftp_get_proxy(struct url * url, const char *flags)
1107{ 1108{
1108 struct url *purl; 1109 struct url *purl;
1109 char *p, *fp, *FP, *hp, *HP; 1110 char *p, *fp, *FP, *hp, *HP;
1110 1111
1111 if (flags != NULL && strchr(flags, 'd') != NULL) 1112 if (flags != NULL && strchr(flags, 'd') != NULL)
1112 return NULL; 1113 return NULL;
1113 if (fetch_no_proxy_match(url->host)) 1114 if (fetch_no_proxy_match(url->host))
1114 return NULL; 1115 return NULL;
1115 1116
1116 FP = getenv("FTP_PROXY"); 1117 FP = getenv("FTP_PROXY");
1117 fp = getenv("ftp_proxy"); 1118 fp = getenv("ftp_proxy");
1118 HP = getenv("HTTP_PROXY"); 1119 HP = getenv("HTTP_PROXY");
1119 hp = getenv("http_proxy"); 1120 hp = getenv("http_proxy");
1120 1121
1121 if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) && 1122 if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) &&
1122 *p && (purl = fetchParseURL(p)) != NULL) { 1123 *p && (purl = fetchParseURL(p)) != NULL) {
1123 if (!*purl->scheme) { 1124 if (!*purl->scheme) {
1124 if (fp || FP) 1125 if (fp || FP)
1125 strcpy(purl->scheme, SCHEME_FTP); 1126 strcpy(purl->scheme, SCHEME_FTP);
1126 else 1127 else
1127 strcpy(purl->scheme, SCHEME_HTTP); 1128 strcpy(purl->scheme, SCHEME_HTTP);
1128 } 1129 }
1129 if (!purl->port) 1130 if (!purl->port)
1130 purl->port = fetch_default_proxy_port(purl->scheme); 1131 purl->port = fetch_default_proxy_port(purl->scheme);
1131 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 1132 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
1132 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 1133 strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
1133 return purl; 1134 return purl;
1134 fetchFreeURL(purl); 1135 fetchFreeURL(purl);
1135 } 1136 }
1136 return NULL; 1137 return NULL;
1137} 1138}
1138 1139
1139/* 1140/*
1140 * Process an FTP request 1141 * Process an FTP request
1141 */ 1142 */
1142fetchIO * 1143fetchIO *
1143ftp_request(struct url *url, const char *op, const char *op_arg, 1144ftp_request(struct url *url, const char *op, const char *op_arg,
1144 struct url_stat *us, struct url *purl, const char *flags) 1145 struct url_stat *us, struct url *purl, const char *flags)
1145{ 1146{
1146 fetchIO *f; 1147 fetchIO *f;
1147 char *path; 1148 char *path;
1148 conn_t *conn; 1149 conn_t *conn;
1149 int if_modified_since, oflag; 1150 int if_modified_since, oflag;
1150 struct url_stat local_us; 1151 struct url_stat local_us;
1151 1152
1152 /* check if we should use HTTP instead */ 1153 /* check if we should use HTTP instead */
1153 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 1154 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
1154 if (strcmp(op, "STAT") == 0) 1155 if (strcmp(op, "STAT") == 0)
1155 return (http_request(url, "HEAD", us, purl, flags)); 1156 return (http_request(url, "HEAD", us, purl, flags));
1156 else if (strcmp(op, "RETR") == 0) 1157 else if (strcmp(op, "RETR") == 0)
1157 return (http_request(url, "GET", us, purl, flags)); 1158 return (http_request(url, "GET", us, purl, flags));
1158 /* 1159 /*
1159 * Our HTTP code doesn't support PUT requests yet, so try 1160 * Our HTTP code doesn't support PUT requests yet, so try
1160 * a direct connection. 1161 * a direct connection.
1161 */ 1162 */
1162 } 1163 }
1163 1164
1164 /* connect to server */ 1165 /* connect to server */
1165 conn = ftp_connect(url, purl, flags); 1166 conn = ftp_connect(url, purl, flags);
1166 if (purl) 1167 if (purl)
1167 fetchFreeURL(purl); 1168 fetchFreeURL(purl);
1168 if (conn == NULL) 1169 if (conn == NULL)
1169 return (NULL); 1170 return (NULL);
1170 1171
1171 if ((path = fetchUnquotePath(url)) == NULL) { 1172 if ((path = fetchUnquotePath(url)) == NULL) {
1172 fetch_syserr(); 1173 fetch_syserr();
1173 return NULL; 1174 return NULL;
1174 } 1175 }
1175 1176
1176 /* change directory */ 1177 /* change directory */
1177 if (ftp_cwd(conn, path, op_arg != NULL) == -1) { 1178 if (ftp_cwd(conn, path, op_arg != NULL) == -1) {
1178 free(path); 1179 free(path);
1179 return (NULL); 1180 return (NULL);
1180 } 1181 }
1181 1182
1182 if_modified_since = CHECK_FLAG('i'); 1183 if_modified_since = CHECK_FLAG('i');
1183 if (if_modified_since && us == NULL) 1184 if (if_modified_since && us == NULL)
1184 us = &local_us; 1185 us = &local_us;
1185 1186
1186 /* stat file */ 1187 /* stat file */
1187 if (us && ftp_stat(conn, path, us) == -1 1188 if (us && ftp_stat(conn, path, us) == -1
1188 && fetchLastErrCode != FETCH_PROTO 1189 && fetchLastErrCode != FETCH_PROTO
1189 && fetchLastErrCode != FETCH_UNAVAIL) { 1190 && fetchLastErrCode != FETCH_UNAVAIL) {
1190 free(path); 1191 free(path);
1191 return (NULL); 1192 return (NULL);
1192 } 1193 }
1193 1194
1194 if (if_modified_since && url->last_modified > 0 && 1195 if (if_modified_since && url->last_modified > 0 &&
1195 url->last_modified >= us->mtime) { 1196 url->last_modified >= us->mtime) {
1196 free(path); 1197 free(path);
1197 fetchLastErrCode = FETCH_UNCHANGED; 1198 fetchLastErrCode = FETCH_UNCHANGED;
1198 snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged"); 1199 snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged");
1199 return NULL; 1200 return NULL;
1200 } 1201 }
1201 1202
1202 /* just a stat */ 1203 /* just a stat */
1203 if (strcmp(op, "STAT") == 0) { 1204 if (strcmp(op, "STAT") == 0) {
1204 free(path); 1205 free(path);
1205 return fetchIO_unopen(NULL, NULL, NULL, NULL); 1206 return fetchIO_unopen(NULL, NULL, NULL, NULL);
1206 } 1207 }
1207 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 1208 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)
1208 oflag = O_WRONLY; 1209 oflag = O_WRONLY;
1209 else 1210 else
1210 oflag = O_RDONLY; 1211 oflag = O_RDONLY;
1211 1212
1212 /* initiate the transfer */ 1213 /* initiate the transfer */
1213 f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags)); 1214 f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags));
1214 free(path); 1215 free(path);
1215 return f; 1216 return f;
1216} 1217}
1217 1218
1218/* 1219/*
1219 * Get and stat file 1220 * Get and stat file
1220 */ 1221 */
1221fetchIO * 1222fetchIO *
1222fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 1223fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
1223{ 1224{
1224 return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags)); 1225 return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags));
1225} 1226}
1226 1227
1227/* 1228/*
1228 * Get file 1229 * Get file
1229 */ 1230 */
1230fetchIO * 1231fetchIO *
1231fetchGetFTP(struct url *url, const char *flags) 1232fetchGetFTP(struct url *url, const char *flags)
1232{ 1233{
1233 return (fetchXGetFTP(url, NULL, flags)); 1234 return (fetchXGetFTP(url, NULL, flags));
1234} 1235}
1235 1236
1236/* 1237/*
1237 * Put file 1238 * Put file
1238 */ 1239 */
1239fetchIO * 1240fetchIO *
1240fetchPutFTP(struct url *url, const char *flags) 1241fetchPutFTP(struct url *url, const char *flags)
1241{ 1242{
1242 return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL, 1243 return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL,
1243 ftp_get_proxy(url, flags), flags)); 1244 ftp_get_proxy(url, flags), flags));
1244} 1245}
1245 1246
1246/* 1247/*
1247 * Get file stats 1248 * Get file stats
1248 */ 1249 */
1249int 1250int
1250fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 1251fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
1251{ 1252{
1252 fetchIO *f; 1253 fetchIO *f;
1253 1254
1254 f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags); 1255 f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags);
1255 if (f == NULL) 1256 if (f == NULL)
1256 return (-1); 1257 return (-1);
1257 fetchIO_close(f); 1258 fetchIO_close(f);
1258 return (0); 1259 return (0);
1259} 1260}
1260 1261
1261/* 1262/*
1262 * List a directory 1263 * List a directory
1263 */ 1264 */
1264int 1265int
1265fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags) 1266fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags)
1266{ 1267{
1267 fetchIO *f; 1268 fetchIO *f;
1268 char buf[2 * PATH_MAX], *eol, *eos; 1269 char buf[2 * PATH_MAX], *eol, *eos;
1269 ssize_t len; 1270 ssize_t len;
1270 size_t cur_off; 1271 size_t cur_off;
1271 int ret; 1272 int ret;
1272 1273
1273 /* XXX What about proxies? */ 1274 /* XXX What about proxies? */
1274 if (pattern == NULL || strcmp(pattern, "*") == 0) 1275 if (pattern == NULL || strcmp(pattern, "*") == 0)
1275 pattern = ""; 1276 pattern = "";
1276 f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags); 1277 f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags);
1277 if (f == NULL) 1278 if (f == NULL)
1278 return -1; 1279 return -1;
1279 1280
1280 cur_off = 0; 1281 cur_off = 0;
1281 ret = 0; 1282 ret = 0;
1282 1283
1283 while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) { 1284 while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) {
1284 cur_off += len; 1285 cur_off += len;
1285 while ((eol = memchr(buf, '\n', cur_off)) != NULL) { 1286 while ((eol = memchr(buf, '\n', cur_off)) != NULL) {
1286 if (len == eol - buf) 1287 if (len == eol - buf)
1287 break; 1288 break;
1288 if (eol != buf) { 1289 if (eol != buf) {
1289 if (eol[-1] == '\r') 1290 if (eol[-1] == '\r')
1290 eos = eol - 1; 1291 eos = eol - 1;
1291 else 1292 else
1292 eos = eol; 1293 eos = eol;
1293 *eos = '\0'; 1294 *eos = '\0';
1294 ret = fetch_add_entry(ue, url, buf, 0); 1295 ret = fetch_add_entry(ue, url, buf, 0);
1295 if (ret) 1296 if (ret)
1296 break; 1297 break;
1297 cur_off -= eol - buf + 1; 1298 cur_off -= eol - buf + 1;
1298 memmove(buf, eol + 1, cur_off); 1299 memmove(buf, eol + 1, cur_off);
1299 } 1300 }
1300 } 1301 }
1301 if (ret) 1302 if (ret)
1302 break; 1303 break;
1303 } 1304 }
1304 if (cur_off != 0 || len < 0) { 1305 if (cur_off != 0 || len < 0) {
1305 /* Not RFC conform, bail out. */ 1306 /* Not RFC conform, bail out. */
1306 fetchIO_close(f); 1307 fetchIO_close(f);
1307 return -1; 1308 return -1;
1308 } 1309 }
1309 fetchIO_close(f); 1310 fetchIO_close(f);
1310 return ret; 1311 return ret;
1311} 1312}

cvs diff -r1.2 -r1.3 src/external/bsd/fetch/dist/libfetch/http.c (switch to unified diff)

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